home *** CD-ROM | disk | FTP | other *** search
Wrap
/* File: PrinterClassDriver.c Contains: MacOS USB printer class driver [ref. IEEE Std 1284-1994] Version: xxx put version here xxx Copyright: 1998 by Apple Computer, Inc., all rights reserved. */ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ includes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ #include <Gestalt.h> #include <Devices.h> #include <DriverServices.h> #include <Interrupts.h> #include <LowMem.h> #include <Folders.h> #include <String.h> #include <stdio.h> #include <USB.h> #ifndef __CODEFRAGMENTS__ #include <codefragments.h> #endif #include "PrinterClassDriver.h" #include "TradDriverLoaderLib.h" /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ constants ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ #define MAX_SUFFIX 127 // maximum copies we'll enter in unit table #define kUSBParallelDrvrRsrcID 12 #define kMinDrvrUnitNumber 48 // minimum unit table entry which we'll install #define kUSS720MillisecondDelay 3*1000 // poll every three seconds #define kUSS720StatusMSDelay 100 // poll ten times per second #define MAX_USB_TRANSFER_SIZE TRANSFER_SIZE // zero acts as manifest for conditional compilation #define kUSBAttributeBulk 0x02 #define kUSBInputEndpointMask 0x80 #define kDebugStatusLevel 5 #define kNormalStatusLevel 4 enum { kUSBv12 = 0x01200000, kUSBv135 = 0x01350000 }; enum { kCString = 0, // StateStr, USBStatusStr selector kPString, // StateStr, USBStatusStr selector kDrvrFirstDigit = 5 // length_byte + ".USB" = 5 }; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ manifest constants ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ // // DEBUGGING extra debugging information to USB Expert Log // DOUBLE_BUFFER use a page aligned buffer in system heap to double buffer app i/o // LOG echo all the write data to a log file in the system folder // LOCK_MEMORY LockMemory on the i/o buffer before write and unlock on write completion // VIRTUAL_MEMORY_CHECK check the physical addresses of the buffer we pass for write requests // MACSBUG_ON_READ break into MacsBug before each read request // MACSBUG_ON_READ_COMPLETE break into MacsBug at each read completion routine // MACSBUG_ON_WRITE break into MacsBug on each write request // MACSBUG_ON_WRITE_COMPLETE break into MacsBug at each write completion routine // #define DEBUGGING 1 /* DEBUGGING */ #define DOUBLE_BUFFER 1 /* DOUBLE_BUFFER */ #define LOG 0 /* LOG */ #define LOCK_MEMORY 1 /* LOCK_MEMORY */ #define MACSBUG_ON_READ 1 /* MACSBUG_ON_READ */ #define MACSBUG_ON_READ_COMPLETE 1 /* MACSBUG_ON_READ_COMPLETE */ #define MACSBUG_ON_WRITE 1 /* MACSBUG_ON_WRITE */ #define MACSBUG_ON_WRITE_COMPLETE 1 /* MACSBUG_ON_WRITE_COMPLETE */ #define VIRTUAL_MEMORY_CHECK 0 /* VIRTUAL_MEMORY_CHECK requires LOCK_MEMORY */ #if LOG #define LOGGING(x) x #include <stdio.h> #else #define LOGGING(x) #endif /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ globals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static struct usbPrinterPBStruct printerClassRecord; static FSSpec printerClassDriverFileSpec; LOGGING( static FILE *logfile ); SInt16 openRef = 0; // used to only suspend on last close volatile enum{ kUSBPrintSuspended, kUSBPrintResumed }wantToBe = kUSBPrintResumed, currentlyAre = kUSBPrintResumed; // These for resuming reads, writes and control calls which were delayed due to resume IOParamPtr Wpb; DCtlPtr Wctl; struct usbPrinterPBStruct *WpPrinterPB; IOParamPtr Rpb; DCtlPtr Rctl; struct usbPrinterPBStruct *RpPrinterPB; IOParamPtr Cpb; DCtlPtr Cctl; struct usbPrinterPBStruct *CpPrinterPB; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ prototypes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void PrinterDeviceCompletionProc(USBPB *pb); static void SetNullUSBParamBlock( USBDeviceRef dev, USBPB *pb ); static void SoftReset( USBPB *usbprint, short interfaceNum ); static void ClearEndpointHalt( USBPB *usbprint); static void CapabilityRequest( USBPB *pb, Ptr p, long length, short config, short interfaceNum, short alternateSetting ); static void CentronicsStatus( USBPB *usbprint, Ptr p, short interfaceNum ); static void CompletionProc(USBPB *pb); static OSStatus MyUSBExpertStatusLevel(UInt32 level, USBDeviceRef ref, StringPtr status, UInt32 value); void PrinterDeviceInitiateTransaction(USBPB *pb); OSErr CFMInitialization( CFragInitBlock *initBlock ); Boolean gUSBVersionNeedsBulkFixPresent; UInt32 gUSBVersion; void CheckUSBVersion(void); OSStatus SecondaryUSBBulkRead(void *pb, void *result); OSStatus SecondaryUSBBulkWrite(void *pb, void *result); OSStatus SafeUSBBulkRead(USBPB *pb); OSStatus SafeUSBBulkWrite(USBPB *pb); void CheckUSBVersion(void) { OSStatus err; err = Gestalt('usbv', (long*)&gUSBVersion); if (err == noErr) gUSBVersionNeedsBulkFixPresent = (gUSBVersion < kUSBv12); } OSStatus SecondaryUSBBulkRead(void *pb, void *result) { *(OSStatus*)result = USBBulkRead((USBPB*)pb); return noErr; } OSStatus SecondaryUSBBulkWrite(void *pb, void *result) { *(OSStatus*)result = USBBulkWrite((USBPB*)pb); return noErr; } OSStatus SafeUSBBulkRead(USBPB *pb) { OSStatus result; pb->usbFlags = 0; // BT we may have set the address request flag if (gUSBVersionNeedsBulkFixPresent) { // Use CallSecondaryInterruptHandler2 to call USBBulkRead if // less than USB v1.2 present CallSecondaryInterruptHandler2(SecondaryUSBBulkRead, nil, pb, &result); } else result = USBBulkRead(pb); return result; } OSStatus SafeUSBBulkWrite(USBPB *pb) { OSStatus result; pb->usbFlags = 0; // BT we may have set the address request flag if (gUSBVersionNeedsBulkFixPresent) { // Use CallSecondaryInterruptHandler2 to call USBBulkWrite if // less than USB v1.2 present CallSecondaryInterruptHandler2(SecondaryUSBBulkWrite, nil, pb, &result); } else result = USBBulkWrite(pb); return result; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: HexString8 Input Parameters: v unsigned long value Output Parameters: p 8 bytes: hex string representing value Description: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void HexString8( unsigned long v, unsigned char *p ) { short shift; for ( shift = 32-4; shift >= 0; shift -= 4 ) { char c = (v >> shift) & 0x0F; *p++ = c + (c > 9? ('A'-10): '0'); } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: hexstr Input Parameters: count number of bytes p pointer to bytes Output Parameters: q hex dump of data Description: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void hexstr( long count, char *p, char *q ) { char *s = p; long i = count; for ( ; count > 0; --count, ++p ) { UInt8 hi, lo; hi = (*p >> 4)& 0x0F; lo = *p & 0x0F; *q++ = '0'; *q++ = 'x'; *q++ = hi + (hi > 9? 'A' - 10: '0'); *q++ = lo + (lo > 9? 'A' - 10: '0'); *q++ = ' '; } for ( ; i > 0; --i, ++s ) *q++ = *s < ' ' || *s > 0x7E? '.': *s; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: ForceUpperStr Input Parameters: s pointer to a null terminated string Output Parameters: s string modified by using toupper() on each character. Description: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void ForceUpperStr(char *s) { while(*s){ *s = toupper(*s); s++; } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: MakeNameRegistrySafeStr Input Parameters: s pointer to a null terminated string Output Parameters: s string modified by replacing / with - on each character. Description: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void MakeNameRegistrySafeStr(char *s) { while (*s) { if (*s == '/') *s = '-'; s++; } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: USBStatusStr Input Parameters: usbStatus usb error code kind kPString or kCString Output Parameters: unsigned char * description of error (c-string or p-string) Description: a simple mapping of errors to human-readable form ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static unsigned char * USBStatusStr( OSStatus usbStatus, short kind ) { unsigned char *p; switch ( usbStatus ) { case kUSBInternalErr: p = kPStrPrinterDriverName"Internal error"; break; case kUSBUnknownDeviceErr: p = kPStrPrinterDriverName"Unknown device"; break; case kUSBUnknownPipeErr: p = kPStrPrinterDriverName"Unknown pipe"; break; case kUSBTooManyPipesErr: p = kPStrPrinterDriverName"Too many pipes"; break; case kUSBIncorrectTypeErr: p = kPStrPrinterDriverName"Incorrect type"; break; case kUSBRqErr: p = kPStrPrinterDriverName"Request error"; break; case kUSBUnknownRequestErr: p = kPStrPrinterDriverName"Unknown request"; break; case kUSBTooManyTransactionsErr: p = kPStrPrinterDriverName"Too many transactions"; break; case kUSBAlreadyOpenErr: p = kPStrPrinterDriverName"Already open"; break; case kUSBNoDeviceErr: p = kPStrPrinterDriverName"No device"; break; case kUSBDeviceErr: p = kPStrPrinterDriverName"Device error"; break; case kUSBOutOfMemoryErr: p = kPStrPrinterDriverName"Out of memory"; break; case kUSBNotFound: p = kPStrPrinterDriverName"Not found"; break; case kUSBPBVersionError: p = kPStrPrinterDriverName"Wrong pbVersion"; break; case kUSBPBLengthError: p = kPStrPrinterDriverName"pbLength too small"; break; case kUSBCompletionError: p = kPStrPrinterDriverName"Missing completion routine"; break; case kUSBFlagsError: p = kPStrPrinterDriverName"Unused flags not zeroed"; break; case kUSBAbortedError: p = kPStrPrinterDriverName"Pipe aborted"; break; case kUSBNoBandwidthError: p = kPStrPrinterDriverName"No bandwidth available"; break; case kUSBPipeIdleError: p = kPStrPrinterDriverName"Pipe is idle"; break; case kUSBPipeStalledError: p = kPStrPrinterDriverName"Pipe is stalled"; break; case kUSBUnknownInterfaceErr: p = kPStrPrinterDriverName"Unknown interfaceRef"; break; case kUSBDeviceBusy: p = kPStrPrinterDriverName"Device is busy"; break; case kUSBDevicePowerProblem: p = kPStrPrinterDriverName"Device power problem"; break; case kUSBInvalidBuffer: p = kPStrPrinterDriverName"Bad buffer (nil?)"; break; case kUSBDeviceSuspended: p = kPStrPrinterDriverName"Device is suspended"; break; case kUSBDeviceNotSuspended: p = kPStrPrinterDriverName"Device is not suspended"; break; case kUSBDeviceDisconnected: p = kPStrPrinterDriverName"Disconnected during suspend/reset"; break; case kUSBTimedOut: p = kPStrPrinterDriverName"Transaction timed out"; break; case kUSBLinkErr: p = kPStrPrinterDriverName"Link Err"; break; case kUSBCRCErr: p = kPStrPrinterDriverName"Comms/Device err, bad CRC"; break; case kUSBBitstufErr: p = kPStrPrinterDriverName"Comms/Device err, bitstuffing"; break; case kUSBDataToggleErr: p = kPStrPrinterDriverName"Comms/Device err, Bad data toggle"; break; case kUSBEndpointStallErr: p = kPStrPrinterDriverName"Device didn't understand"; break; case kUSBNotRespondingErr: p = kPStrPrinterDriverName"No device, device hung"; break; case kUSBPIDCheckErr: p = kPStrPrinterDriverName"Comms/Device err, PID CRC error"; break; case kUSBWrongPIDErr: p = kPStrPrinterDriverName"Comms/Device err, Bad or wrong PID"; break; case kUSBOverRunErr: p = kPStrPrinterDriverName"Packet too large or more data than buffer"; break; case kUSBUnderRunErr: p = kPStrPrinterDriverName"Less data than buffer"; break; case kUSBRes1Err: p = kPStrPrinterDriverName"kUSBRes1Err"; break; case kUSBRes2Err: p = kPStrPrinterDriverName"kUSBRes1Err"; break; case kUSBBufOvrRunErr: p = kPStrPrinterDriverName"Buffer over run error"; break; case kUSBBufUnderRunErr: p = kPStrPrinterDriverName"Buffer under run error"; break; case kUSBNotSent1Err: p = kPStrPrinterDriverName"Transaction not sent1"; break; case kUSBNotSent2Err: p = kPStrPrinterDriverName"Transaction not sent2"; break; default: p = kPStrPrinterDriverName"Unknown error nnnnnnnn"; HexString8( usbStatus, p + *p - 8 + 1 ); break; } return kind == kPString? p: p + 1; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: StateStr Input Parameters: usbRefCon usb error code kind kPString or kCString Output Parameters: unsigned char * description of state (c-string or p-string) Description: a simple mapping of states to human-readable form ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static unsigned char * StateStr( OSStatus refCon, short kind ) { unsigned char *p; refCon &= ~(kTransactionPending | kRetryTransaction | kReturnFromDriver); switch ( refCon ) { case kUndefined: p = kPStrPrinterDriverName"kUndefined"; break; case kNilCompletion: p = kPStrPrinterDriverName"kNilCompletion"; break; case kFindInterface_bidirectional: p = kPStrPrinterDriverName"kFindInterface_bidirectional"; break; case kFindInterface_unidirectional: p = kPStrPrinterDriverName"kFindInterface_unidirectional"; break; case kOpenDevice: p = kPStrPrinterDriverName"kOpenDevice"; break; case kNewInterfaceRef: p = kPStrPrinterDriverName"kNewInterfaceRef"; break; case kSetInterface: p = kPStrPrinterDriverName"kSetInterface"; break; case kConfigureInterface: p = kPStrPrinterDriverName"kConfigureInterface"; break; case kGetCapabilityString: p = kPStrPrinterDriverName"kGetCapabilityString"; break; case kDelayGetCapability: p = kPStrPrinterDriverName"kDelayGetCapability"; break; case kAllocateCapabilityMem: p = kPStrPrinterDriverName"kAllocateCapabilityMem"; break; case kGetFullCapabilityString: p = kPStrPrinterDriverName"kGetFullCapabilityString"; break; case kGetInterface: p = kPStrPrinterDriverName"kGetInterface"; break; case kFindBulkOutPipe: p = kPStrPrinterDriverName"kFindBulkOutPipe"; break; case kFindBulkInPipe: p = kPStrPrinterDriverName"kFindBulkInPipe"; break; case kTaskTimeRequired: p = kPStrPrinterDriverName"kTaskTimeRequired"; break; case kGetCentronicsStatus: p = kPStrPrinterDriverName"kGetCentronicsStatus"; break; case kDelayGetCentronicsStatus: p = kPStrPrinterDriverName"kDelayGetCentronicsStatus"; break; case kDeallocateCapbilityString: p = kPStrPrinterDriverName"kDeallocateCapbilityString"; break; default: p = kPStrPrinterDriverName"Unknown state nnnnnnnn"; HexString8( refCon, p + *p - 8 + 1 ); break; } return kind == kPString? p: p + 1; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: HiHex Input Parameters: v only low byte used Output Parameters: <function result> high nibble represented as ASCII char Description: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static unsigned char HiHex( unsigned char v ) { unsigned char hinibble = (v >> 4) & 0x0f; return hinibble + ((hinibble > 9)? ('A'-10): '0'); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: LoHex Input Parameters: v only low byte used Output Parameters: <function result> low nibble represented as ASCII char Description: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static unsigned char LoHex( unsigned char v ) { unsigned char lonibble = v & 0x0f; return lonibble + ((lonibble > 9)? ('A'-10): '0'); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: immediateError Input Parameters: Output Parameters: Description: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static Boolean immediateError(OSStatus err) { return((err != kUSBPending) && (err != noErr) ); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: SetNullUSBParamBlock Input Parameters: pb pointer to USB parameter block dev USB device reference Output Parameters: pb all fields set to default values Description: setup a USB parameter block for use by the current device ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void SetNullUSBParamBlock( USBDeviceRef dev, USBPB *pb ) { pb->qlink = NULL; pb->qType = 0; pb->pbLength = sizeof(USBPB); pb->pbVersion = kUSBCurrentPBVersion; pb->usbFlags = 0; pb->usbStatus = noErr; pb->usbCompletion = (USBCompletion) NULL; pb->usbReference = dev; } // BT - 15Jun99, Callback for the suspend call. static void usbPrintResume(USBPB *pb) { IOParamPtr resumePB; DCtlPtr resumeCtl; struct usbPrinterPBStruct *resumePrinterPB; if(pb->usbStatus != noErr) { MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, "\pUSB print driver USBPrintDoSuspendResume error", pb->usbStatus); } else { currentlyAre = kUSBPrintResumed; if(Wpb != nil) { // We have to restart a waiting write MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, "\pUSB print driver resumeing write", 0); resumeCtl = Wctl; resumePrinterPB = WpPrinterPB; resumePB = Wpb; Wpb = nil; QueueWrite(resumePB, resumeCtl, resumePrinterPB); } if(Rpb != nil) { // We have to restart a waiting write MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, "\pUSB print driver resumeing read", 0); resumeCtl = Rctl; resumePrinterPB = RpPrinterPB; resumePB = Rpb; Rpb = nil; QueueRead(resumePB, resumeCtl, resumePrinterPB); } if(Cpb != nil) { // We have to restart a waiting ControlRequest MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, "\pUSB print driver resumeing read", 0); resumeCtl = Cctl; resumePrinterPB = CpPrinterPB; resumePB = Cpb; Cpb = nil; ControlStatusRequests(resumePB, resumeCtl, resumePrinterPB); } } pb->usbRefcon = 0; } // BT - 15Jun99, work out if we need to and can suspend or resume and do it. static USBPrintDoSuspendResume(USBDeviceRef ref) { static USBPB pb; OSStatus err = noErr; if(wantToBe != currentlyAre) { if(currentlyAre == kUSBPrintSuspended) { MyUSBExpertStatusLevel(kDebugStatusLevel, ref, "\pUSB print driver resuming device", ref); err = USBResumeDeviceByReference(ref); } else if(wantToBe == kUSBPrintSuspended) { if( (printerClassRecord.pb.usbCompletion != nil) || (printerClassRecord.in.usbCompletion != nil) || (printerClassRecord.out.usbCompletion != nil) ) { MyUSBExpertStatusLevel(kDebugStatusLevel, ref, "\pUSB print driver can't suspend, busy", (UInt32) printerClassRecord.pb.usbCompletion); return(noErr); } if(pb.usbRefcon != 0) { MyUSBExpertStatusLevel(kDebugStatusLevel, ref, "\pUSB print driver suspend already in process", err); return(noErr); } MyUSBExpertStatusLevel(kDebugStatusLevel, ref, "\pUSB print driver suspending device", ref); SetNullUSBParamBlock(ref, &pb); pb.usbCompletion = usbPrintResume; pb.usbRefcon = 1; currentlyAre = kUSBPrintSuspended; err = USBSuspendDevice(&pb); } if( (err != kUSBPending) && (err != noErr) ) { MyUSBExpertStatusLevel(2, ref, "\pUSB print driver USBPrintDoSuspendResume error", err); } } return(noErr); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: ParseCapability Input Parameters: capability pointer to 1284-1994 capability string attribute pointer to c-string key we're retrieving (terminate with colon) *result_length maximum length of the result Output Parameters: result c-string which followed the key and was terminated by semi-colon (semi-colon has been stripped) *result_length actual length of the result (may be zero) Description: Search for "attribute" in the 1284 "capability" string The identifier is terminated with a semi-colon Return the attribute in result null-string if attribute not found. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void ParseCapability( unsigned char *capability, unsigned char *attribute, unsigned char *result, short *result_length ) { // // search for "attribute" in the 1284 "capability" string // // the identifier is terminated with a semi-colon // // return the attribute in result // null-string if attribute not found // // <to do> skip blanks, isolate colon, skip blanks // UInt32 source_length; UInt16 target_length; unsigned char *source, *target, *destination; Boolean foundAttribute = false; // // begin a brute force search // source = attribute; source_length = CStrLen((char *)attribute ); target = capability + 2; // skip the length target_length = *(UInt16*)capability; if (target_length >= 2) target_length -= 2; // don't count the length else target_length = 0; while ( target_length >= source_length ) { if ( memcmp( source, target, source_length ) == 0 ){ foundAttribute = true; break; } --target_length; ++target; } *result = '\0'; // empty result if (foundAttribute) // we found the attribute string we are looking for, let's copy the result { destination = result; target += source_length; target_length -= source_length; // skip the attribute string if ( target_length > 0 ) { // // we found the attribute in the capability string // while ( destination - result < *result_length ) // arbitrarily limit reply { if ( *target == ';' || target_length == 0) break; *destination++ = *target++; // copy a byte of attribute over --target_length; } *destination++ = '\0'; // trailing NUL *result_length = destination - result; // report the length of the data (with trailing NUL) } } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: GetClass Input Parameters: capability pointer to 1284-1994 capability string *result_length maximum length of the result Output Parameters: result c-string extracted from the MODEL key *result_length actual length of the result (may be zero) Description: CLASS is a Microsoft extension to the 1284-capability string if we don't find it we assume that the device is a printer Since the USB hardware has already indicated this, we're really allowing devices which aren't printers to be reached via the DRVRs we've installed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void GetClass( unsigned char *capability, unsigned char *result, short *result_length ) { // // We supply the upper-case PRINTER to be consistent with 1284-1994 // if we don't find a class. // ParseCapability( capability, (unsigned char *) "CLASS:", result, result_length); if ( *result == '\0' ) ParseCapability( capability, (unsigned char *) "CLS:", result, result_length); if ( *result == '\0' ) CStrCopy( (char *)result, "PRINTER" ); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: GetModel Input Parameters: capability pointer to 1284-1994 capability string *result_length maximum length of the result Output Parameters: result c-string extracted from the MODEL key *result_length actual length of the result (may be zero) Description: MODEL is a required field of the 1284-capability string MODEL may be abbreviated MDL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void GetModel( unsigned char *capability, unsigned char *result, short *result_length ) { // // Most manufacturers abbreviate, so we search for MDL first // ParseCapability( capability, (unsigned char *) "MDL:", result, result_length ); if ( *result == '\0' ) ParseCapability( capability, (unsigned char *) "MODEL:", result, result_length ); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: GetCommandSet Input Parameters: Output Parameters: Description: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void GetCommandSet( unsigned char *capability, unsigned char *result, short *result_length ) { // // COMMAND-SET is a required field of the 1284-capability string // COMMAND-SET may be abbreviated CMD // ParseCapability( capability, (unsigned char *) "CMD:", result, result_length ); if ( *result == '\0' ) ParseCapability( capability, (unsigned char *) "COMMAND SET:", result, result_length ); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: GetManufacturer Input Parameters: Output Parameters: Description: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void GetManufacturer( unsigned char *capability, unsigned char *result, short *result_length ) { // // MANUFACTURER is a required field of the 1284-capability string // MANUFACTURER may be abbreviated MFG // ParseCapability( capability, (unsigned char *) "MFG:", result, result_length ); if ( *result == '\0' ) ParseCapability( capability, (unsigned char *) "MANUFACTURER:", result, result_length ); if ( *result == '\0' ) ParseCapability( capability, (unsigned char *) "HMFG:", result, result_length ); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: DeregisterDevice Input Parameters: pPrinterPB Output Parameters: <none> Description: remove the device from the name registry ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ #define kMinNameLength 27 #define kMinModelLength 25 #define kMinClassLength 23 static OSStatus DeregisterDevice( struct usbPrinterPBStruct *pPrinterPB ) { RegEntryID self, child, parent; RegEntryIter cookie; OSStatus err = noErr; Str255 tempStr1,tempStr2; UInt32 nameLength, modelLength,devclassLength; Boolean done = false; MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Removing the printer from the name registry", 0 ); // delete the printer node // print out the printer's name (which is the same as the node name) nameLength = CStrLen((char *)pPrinterPB->name); CStrCopy((char *)tempStr1, (char *)(kCStrPrinterDriverName"Printer name: ")); CStrCat((char *)tempStr1, (char *)pPrinterPB->name); CStrToPStr((unsigned char *)tempStr2, (char *)tempStr1 ); MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, tempStr2, nameLength ); // locate the model node (parent of printer), and delete it if the printer has no siblings if (nameLength<kMinNameLength) { USBExpertFatalError(pPrinterPB->deviceRef, err, kPStrPrinterDriverName"Warning!! Printer name string is not long enough!", nameLength); } else { RegistryEntryIDInit( &self ); err = RegistryCStrEntryLookup( nil, (char const *) pPrinterPB->name , &self ); if ( err == noErr ) { err = RegistryEntryDelete( &self); } else { MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Printer node not found!", err ); } RegistryEntryIDDispose(&self); } // print out the printer's model (which is the same as the node's parent) modelLength = CStrLen((char *)pPrinterPB->model); CStrCopy((char *)tempStr1, (char *)(kCStrPrinterDriverName"Printer model: ")); CStrCat((char *)tempStr1, (char *)pPrinterPB->model); CStrToPStr((unsigned char *)tempStr2, (char *)tempStr1 ); MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, tempStr2, modelLength ); // locate the model node (parent of printer), and delete it if the printer has no siblings if (modelLength<kMinModelLength) { USBExpertFatalError(pPrinterPB->deviceRef, err, kPStrPrinterDriverName"Warning!! Printer model string is not long enough!", modelLength); } else { RegistryEntryIDInit( &parent ); err = RegistryCStrEntryLookup( nil, (char const *) pPrinterPB->model , &parent ); if ( err == noErr ) { RegistryEntryIDInit(&child); err = RegistryEntryIterateCreate(&cookie); if (err == noErr) err = RegistryEntryIterateSet(&cookie, &parent); if (err == noErr) err = RegistryEntryIterate(&cookie, kRegIterChildren, &child, &done); if ((err == noErr) && done) { MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"No siblings detected, so delete the parent of printer", 0 ); err = RegistryEntryDelete(&parent); } else { MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Siblings (or error) detected. Leaving parent in place", err ); } RegistryEntryIterateDispose(&cookie); } else { MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Parent node not found!", err ); } RegistryEntryIDDispose(&parent); } // print out the printer's model (which is the same as the node's parent) devclassLength = CStrLen((char *)pPrinterPB->devclass); CStrCopy((char *)tempStr1, (char *)(kCStrPrinterDriverName"Printer class: ")); CStrCat((char *)tempStr1, (char *)pPrinterPB->devclass); CStrToPStr((unsigned char *)tempStr2, (char *)tempStr1 ); MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, tempStr2, devclassLength ); // locate the class node (parent of model), and delete it if the class has no siblings if (devclassLength<kMinClassLength) { USBExpertFatalError(pPrinterPB->deviceRef, err, kPStrPrinterDriverName"Warning!! Printer class string not long enough!", devclassLength); } else { RegistryEntryIDInit( &parent ); err = RegistryCStrEntryLookup( nil, (char const *) pPrinterPB->devclass , &parent ); if ( err == noErr ) { RegistryEntryIDInit(&child); err = RegistryEntryIterateCreate(&cookie); if (err == noErr) err = RegistryEntryIterateSet(&cookie, &parent); if (err == noErr) err = RegistryEntryIterate(&cookie, kRegIterChildren, &child, &done); if ((err == noErr) && done) { MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"No models found, so delete the class node", 0 ); err = RegistryEntryDelete(&parent); } else { MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Models detected. Leaving class nodes in place", err ); } RegistryEntryIterateDispose(&cookie); } else { MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Parent node not found!", err ); } RegistryEntryIDDispose(&parent); } return err; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: RegisterDevice Input Parameters: pPrinterPB Output Parameters: <none> Description: add the device into the name registry; we can't insert just the node if the parent nodes aren't present. if it isn't present insert the device CLASS into the registry if it isn't present insert the device MODEL into the registry finally insert the device itself into the registry we're careful to rename multiple devices ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static OSStatus RegisterDevice( struct usbPrinterPBStruct *pPrinterPB ) { // // add the device into the name registry // if it isn't present // insert the device CLASS into the registry // if it isn't present // insert the device MODEL into the registry // finally insert the device itself into the registry // we're careful to rename multiple devices // Str255 identification, devclass, model, nameregmodel, commands, tempstr; RegEntryID parent, where, self; OSStatus err; short model_length, command_length, class_length; MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Adding the printer to the name registry", 0 ); CStrCopy((char *)pPrinterPB->devclass, ""); CStrCopy((char *)pPrinterPB->model, ""); CStrCopy((char *)pPrinterPB->name, ""); command_length = sizeof(commands); GetCommandSet(pPrinterPB->pCapabilityString, commands, &command_length ); // get device class and add it to the name registry. Typically PRINTER or Printer class_length = sizeof(devclass); GetClass( pPrinterPB->pCapabilityString, devclass, &class_length ); // force the device class to all uppercase. ForceUpperStr((char *)devclass); CStrCopy( (char *)pPrinterPB->devclass, (char *)"Devices:device-tree:" ); CStrCat( (char *)pPrinterPB->devclass, (char *)devclass ); RegistryEntryIDInit( &where ); err = RegistryCStrEntryLookup( nil, (char const *) pPrinterPB->devclass, &where ); if ( err == nrPathNotFound ) { // class not in registry // create a node for all devices of this CLASS // RegistryEntryIDInit( &parent ); err = RegistryCStrEntryLookup( nil, "Devices:device-tree:" , &parent ); if ( err == noErr ) err = RegistryCStrEntryCreate( nil, (char const *) pPrinterPB->devclass, &where ); RegistryEntryIDDispose(&parent); } CStrToPStr(tempstr, (char *)pPrinterPB->devclass); MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, tempstr, 0 ); // // add this model into the name registry of this class // if ( err == noErr ) { model_length = sizeof(model); GetModel( pPrinterPB->pCapabilityString, model, &model_length ); CStrCopy( (char *)nameregmodel, (char *)model); MakeNameRegistrySafeStr((char *)nameregmodel); if ( *model != '\0' ) { CStrCopy( (char *)pPrinterPB->model, "Devices:device-tree:" ); CStrCat( (char *)pPrinterPB->model, (char *)devclass ); CStrCat( (char *)pPrinterPB->model, ":" ); if ( *model == '\0' ) { CStrCat( (char *)pPrinterPB->model, (char *)"USB_PRINTERCLASS" ); } else { CStrCat( (char *)pPrinterPB->model, (char *)nameregmodel ); } RegistryEntryIDInit( &self ); err = RegistryCStrEntryLookup( nil, (char const *) pPrinterPB->model , &self ); if ( err != noErr ) { // model not in registry err = RegistryCStrEntryCreate( nil, (char const *) pPrinterPB->model, &self ); } RegistryEntryIDDispose(&self); } CStrToPStr(tempstr, (char *)pPrinterPB->model); MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, tempstr, 0 ); } // // add this instance of this model of this class // into the name registry // if ( *model == '\0' ) { // // possibly the cable isn't firmly connected or the printer isn't switched on // err = kUSBInternalErr; USBExpertFatalError(pPrinterPB->deviceRef, err, kPStrPrinterDriverName"Model undefined", 0); } if ( err == noErr ) { // // start off by identifying the device simply by the model name // increment the numeric suffix until we successfully registry the device // Str32 suffix; short numeric_suffix; CStrCopy( (char *)identification, (char *)nameregmodel ); RegistryEntryIDInit( &self ); for ( numeric_suffix = 1; numeric_suffix < MAX_SUFFIX; ++numeric_suffix ) { CStrCopy( (char *)pPrinterPB->name, "Devices:device-tree:" ); CStrCat( (char *)pPrinterPB->name, (char *)devclass ); CStrCat( (char *)pPrinterPB->name, ":" ); CStrCat( (char *)pPrinterPB->name, (char *)nameregmodel ); CStrCat( (char *)pPrinterPB->name, ":" ); CStrCat( (char *)pPrinterPB->name, (char *)identification ); err = RegistryCStrEntryLookup( nil, (char const *) pPrinterPB->name , &self ); if ( err != noErr ) { // enter device in registry err = RegistryCStrEntryCreate( nil, (char const *) pPrinterPB->name, &self ); if ( err == noErr ) { CStrCopy( (char *)identification, (char *)model ); if (numeric_suffix > 1) { sprintf( (char *)suffix, " %d", numeric_suffix-1 ); CStrCat( (char *)identification, (char *)suffix ); } err = RegistryPropertyCreate( &self, "modelName", identification, strlen((char *)identification) ); } if ( err == noErr ) err = RegistryPropertyCreate( &self, "drvrOut", &pPrinterPB->driverOutName, 1 + pPrinterPB->driverOutName[0] ); if ( err == noErr ) err = RegistryPropertyCreate( &self, "outRef", &pPrinterPB->outRefNum, sizeof(pPrinterPB->outRefNum) ); if (pPrinterPB->printerProtocol != kUSBPrinterUnidirectionalProtocol) { if ( err == noErr ) err = RegistryPropertyCreate( &self, "drvrIn", &pPrinterPB->driverInName, 1 + pPrinterPB->driverInName[0] ); if ( err == noErr ) err = RegistryPropertyCreate( &self, "inRef", &pPrinterPB->inRefNum, sizeof(pPrinterPB->inRefNum) ); } if ( err == noErr ) err = RegistryPropertyCreate( &self, "privateData", &pPrinterPB, sizeof(pPrinterPB) ); if ( err == noErr ) err = RegistryPropertyCreate( &self, "read", &pPrinterPB->r, sizeof(QueueUSBReadUPP) ); if ( err == noErr ) err = RegistryPropertyCreate( &self, "write", &pPrinterPB->w, sizeof(QueueUSBWriteUPP) ); if ( err == noErr && *commands != '\0' ) err = RegistryPropertyCreate( &self, "command-set", &commands, command_length ); break; } // // if this name didn't succeed // try the next numeric suffix // sprintf( (char *)suffix, " %d", numeric_suffix ); CStrCopy( (char *)identification, (char *)nameregmodel ); CStrCat( (char *)identification, (char *)suffix ); } RegistryEntryIDDispose(&self); } pPrinterPB->printerRegistered = true; CStrToPStr((unsigned char *)tempstr, (char const *)pPrinterPB->name ); MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Added printer to name registry", 0 ); MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, tempstr, err ); RegistryEntryIDDispose(&where); LOGGING( logfile = fopen( "USBPrinter.log", "wb+" ) ); return err; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: LoadResources Input Parameters: pPrinterPB pointer to class driver's private storage Output Parameters: OSStatus resource load error Description: Initialize time is the only safe time to to load resources from our file. Here's where we load resources and detach them from the resource manager. Later we'll use these private copies instead of GetResource calls. Using the file spec that we got from the CFMInitialization routine, open our resource fork. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static OSStatus LoadResources( struct usbPrinterPBStruct *pPrinterPB ) { short rf = -1; // class driver resource fork OSStatus err; // // open the resource fork of this class driver // rf = FSpOpenResFile( &printerClassDriverFileSpec,fsRdPerm ); err = ResError(); // // load the DRVR that we'll stick in the unit table // pPrinterPB->hDrvr = NULL; if ( err == noErr ) { pPrinterPB->hDrvr = (DRVRHeaderHandle) Get1Resource( 'DRVR', kUSBParallelDrvrRsrcID ); if ( pPrinterPB->hDrvr != NULL ) DetachResource( (Handle) pPrinterPB->hDrvr ); err = ResError(); } if ( rf != -1 ) CloseResFile( rf ); return err; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: InstallDrivers Input Parameters: pPrinterPB Output Parameters: OSStatus error code Description: install read and write drivers in the unit table we have separate drivers so that we can support a full-duplex protocol rename the driver we just installed (so multiple copies don't conflict) We use the DRVR -refnum-1 so that the driver name has the same hex chars as the driver number (for manual verification in MacsBug) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static OSStatus InstallDrivers( struct usbPrinterPBStruct *pPrinterPB ) { // // install the driver in the unit table // rename the driver we just installed (so multiple copies don't conflict) // short refNum; // DRVR refNum OSStatus err = paramErr; // couldn't find driver if ( pPrinterPB->hDrvr != NULL ) err = TradInstallDriverFromHandle( pPrinterPB->hDrvr, kMinDrvrUnitNumber, TradHighestUnitNumber() + 1, &pPrinterPB->outRefNum ); if ( err == noErr ) { UnitNumber unit; DriverFlags flags; DRVRHeaderPtr hdr; unsigned char *suffix, // append "In" and "Out" *infix; // driver reference number follows the ".USB" TradGetDriverInformation( pPrinterPB->outRefNum, &unit, &flags, pPrinterPB->driverOutName, &hdr ); BlockMove( pPrinterPB->driverOutName, pPrinterPB->driverInName, 1 + pPrinterPB->driverOutName[0] ); suffix = pPrinterPB->driverOutName + pPrinterPB->driverOutName[0] - 2; infix = pPrinterPB->driverOutName + kDrvrFirstDigit; infix[0] = HiHex( -pPrinterPB->outRefNum -1 ); infix[1] = LoHex( -pPrinterPB->outRefNum -1 ); suffix[0] = 'O'; suffix[1] = 'u'; suffix[2] = 't'; TradRenameDriver( pPrinterPB->outRefNum, pPrinterPB->driverOutName ); // // set the driver data to point to our parameter block // err = OpenDriver( pPrinterPB->driverOutName, &refNum ); if ( err == noErr ) { struct usbPrinterPBStruct *param = pPrinterPB; err = Control( refNum, kDrvrPrivateSetStorage, ¶m ); CloseDriver( refNum ); } err = TradInstallDriverFromHandle( pPrinterPB->hDrvr, kMinDrvrUnitNumber, TradHighestUnitNumber() + 1, &pPrinterPB->inRefNum ); if ( err == noErr ) { suffix = pPrinterPB->driverInName + pPrinterPB->driverInName[0] - 1; infix = pPrinterPB->driverInName + kDrvrFirstDigit; infix[0] = HiHex( -pPrinterPB->inRefNum -1 ); infix[1] = LoHex( -pPrinterPB->inRefNum -1 ); suffix[0] = 'I'; suffix[1] = 'n'; TradRenameDriver( pPrinterPB->inRefNum, pPrinterPB->driverInName ); // // set the driver data to point to our parameter block // err = OpenDriver( pPrinterPB->driverInName, &refNum ); if ( err == noErr ) { struct usbPrinterPBStruct *param = pPrinterPB; err = Control( refNum, kDrvrPrivateSetStorage, ¶m ); CloseDriver( refNum ); } } } return err; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: GetCapability Input Parameters: Output Parameters: Description: Asynchronous completion. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void GetCapability( struct usbPrinterPBStruct *pPrinterPB, unsigned char *p, short length ) { // // queue a transaction to retrieve the 1284 capability string // we must have assigned the interface by this point since the capability string // may vary with the interface // OSStatus err; USBPB *pb = &pPrinterPB->pb; SetNullUSBParamBlock( pPrinterPB->deviceRef, &pPrinterPB->pb ); pb->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBIn, kUSBClass, kUSBInterface); pb->usbBuffer = 0; pb->usbActCount = 0; pb->usbReqCount = 0; pb->usb.cntl.BRequest = kUSBPrintClassGetDeviceID; pb->usb.cntl.WValue = 0; // configuration pb->usb.cntl.WIndex = (pPrinterPB->interfaceNumber<<8) | pPrinterPB->alternateSetting; pb->usbReqCount = length; pb->usbBuffer = p; pb->usbRefcon |= kTransactionPending; pb->usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; err = USBDeviceRequest(pb); if(immediateError(err)) { USBExpertFatalError(pb->usbReference, err, kPStrPrinterDriverName"GetCapability", 0); pb->usbCompletion = (USBCompletion) NULL; // checked by Finalize pb->usbRefcon |= kReturnFromDriver; } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: IsLucentCable Input Parameters: dev USB device descriptor Output Parameters: Return 1 if the USB device is a USB-parallel cable Description: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static short IsLucentCable( USBDeviceDescriptor *dev ) { return ( USBToHostWord(dev->vendor) == 0x47E && USBToHostWord(dev->product) == 0x1001 )? 1: 0; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: GetInterface Input Parameters: Output Parameters: Description: Asynchronous completion. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void GetInterface( USBPB *pb, UInt8 *alt ) { // // make sure we account for any alternate interface currently in use by the device // OSStatus err; pb->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBIn, kUSBStandard, kUSBInterface); pb->usb.cntl.BRequest = kUSBRqGetInterface; pb->usb.cntl.WValue = 0; pb->usb.cntl.WIndex = 0; pb->usbReqCount = sizeof(UInt8); // single byte is requested pb->usbBuffer = alt; pb->usbStatus = 0; pb->usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; pb->usbRefcon |= kTransactionPending; err = USBDeviceRequest(pb); if(immediateError(err)) { USBExpertFatalError(pb->usbReference, err, kPStrPrinterDriverName"GetInterface", 0); pb->usbCompletion = (USBCompletion) NULL; // checked by Finalize pb->usbRefcon |= kReturnFromDriver; } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: ReadCompletion Input Parameters: pb USB parameter block Output Parameters: <none> Description: USB read requests complete here. We dequeue the MacOS DRVR read requests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void ReadCompletion(USBPB *pb) { // call the user completion routine struct usbPrinterPBStruct *pPrinterPB = (struct usbPrinterPBStruct *) pb->usbRefcon; IOParamPtr clientParam = pPrinterPB->readDrvr.pb; OSStatus err = pb->usbStatus; DCtlPtr ctlLocal; #if MACSBUG_ON_READ_COMPLETE Str255 text; sprintf( (char *)text, " PrinterClass Read Complete(%d): %d", pb->usbStatus, pb->usbActCount ); text[0] = CStrLen((char *)text); // c2pstr DebugStr( text ); #endif #if DOUBLE_BUFFER // // copy the user data from our page aligned buffer // MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, kPStrPrinterDriverName"ReadCompletion - bytes read " , (UInt32)pb->usbActCount ); if ( pb->usbActCount > 0 ) BlockCopy( pPrinterPB->pageReadAlignedBuffer, clientParam->ioBuffer + clientParam->ioActCount, pb->usbActCount ); #endif // update the amount of data actually transferred clientParam->ioActCount += pb->usbActCount; switch( pb->usbStatus ) { // // only retry low level hardware problems // note: abort transactions will end up here as well // case kUSBLinkErr: case kUSBCRCErr: /* Pipe stall, bad CRC */ case kUSBBitstufErr: /* Pipe stall, bitstuffing */ case kUSBDataToggleErr: /* Pipe stall, Bad data toggle */ case kUSBNotRespondingErr: /* Pipe stall, No device, device hung */ case kUSBPIDCheckErr: /* Pipe stall, PID CRC error */ case kUSBWrongPIDErr: /* Pipe stall, Bad or wrong PID */ if ( --(pPrinterPB->readRetryCount) >= 0 ) // = is there so we don't retry clear endpoints forever { if(pPrinterPB->readStatus == noErr) { pPrinterPB->readStatus = pb->usbStatus; } // we got an error, and we still want to retry, clear any stalls MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, kPStrPrinterDriverName"ReadCompletion retry" , pb->usbStatus ); USBClearPipeStallByReference(pPrinterPB->readPipeRef); pb->usbStatus = noErr; MyUSBExpertStatusLevel(3, pb->usbReference, kPStrPrinterDriverName"**** ReadCompletion clearing endpoint halt *****" , pb->usbStatus ); ClearEndpointHalt(pb); // Sets up PB USBDeviceRequest(pb); return; } break; case kUSBAbortedError: /* user cancel, or hot unplug */ MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, kPStrPrinterDriverName"ReadCompletion abortedError" , pb->usbStatus ); USBClearPipeStallByReference(pPrinterPB->readPipeRef); USBClearPipeStallByReference(pPrinterPB->deviceRef); break; // // any other error will be reported to the client // default: case noErr: break; } // (pPrinterPB->readStatus != noErr) //or // (pPrinterPB->readStatus == noErr) and (pb->usbActCount == pb->usbReqCount) // read Status == noErr // true false // // act=req t r r // // f x r if ( (pb->usbStatus == noErr) && // there were no problems (pPrinterPB->readRetryCount > 0) && // there were no enough problems ( (pPrinterPB->readStatus != noErr) || (pb->usbActCount == pb->usbReqCount) ) && // we got all the data we requested // BT don't do this or we'll never retry read errors. However we need to do this to finish short packets. // (clientParam->ioActCount < clientParam->ioReqCount) ) // we still want more data { // // haven't finish the client's request // update the pointers and start another bulk read transaction // #if LOCK_MEMORY err = UnlockMemory( pb->usbBuffer, pb->usbReqCount ); IF_DEBUG( if ( err != noErr ) DebugStr( kPStrPrinterDriverName"ReadCompletion UnlockMem failed" ) ); #endif pb->usbBuffer = clientParam->ioBuffer + clientParam->ioActCount; pb->usbReqCount = clientParam->ioReqCount - clientParam->ioActCount; #if MAX_USB_TRANSFER_SIZE if ( pb->usbReqCount > MAX_USB_TRANSFER_SIZE ) pb->usbReqCount = MAX_USB_TRANSFER_SIZE; #endif #if DOUBLE_BUFFER // // make sure we have enough room in our buffer // if ( pb->usbReqCount > pPrinterPB->pageReadAlignedBufferSize ) pb->usbReqCount = pPrinterPB->pageReadAlignedBufferSize; pb->usbBuffer = pPrinterPB->pageReadAlignedBuffer; #endif pPrinterPB->readStatus = noErr; #if LOCK_MEMORY err = LockMemory( pb->usbBuffer, pb->usbReqCount ); IF_DEBUG( if ( err != noErr ) DebugStr( kPStrPrinterDriverName"ReadCompletion LockMem failed" ) ); if ( err == noErr ) #endif err = SafeUSBBulkRead( pb ); // Note this is probably hanging off the if above. if ( immediateError(err) ) { MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, kPStrPrinterDriverName"ReadCompletion finish immed err" , err ); pb->usbCompletion = (USBCompletion) NULL; // checked by Finalize if ( pPrinterPB->readDrvr.ctl != NULL ) { ctlLocal = pPrinterPB->readDrvr.ctl; pPrinterPB->readDrvr.ctl = NULL; CallUniversalProc( LMGetJIODone(), uppIODoneProcInfo, err, clientParam, ctlLocal ); } } } else { if(pPrinterPB->readStatus != noErr) { pb->usbStatus = pPrinterPB->readStatus; } // // either we have an error which we're not retrying // or we successfully completed // #if LOCK_MEMORY err = UnlockMemory( pb->usbBuffer, pb->usbReqCount ); IF_DEBUG( if ( err != noErr ) DebugStr( kPStrPrinterDriverName"ReadCompletion concluded UnlockMem failed" ) ); #endif IF_DEBUG( if (pb->usbStatus != noErr) MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, kPStrPrinterDriverName"ReadCompletion Error" , pb->usbStatus ) ); pb->usbCompletion = (USBCompletion) NULL; // checked by Finalize MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, kPStrPrinterDriverName"readDrvr.ctl = " , (UInt32)pPrinterPB->readDrvr.ctl ); if ( pPrinterPB->readDrvr.ctl != NULL ) { ctlLocal = pPrinterPB->readDrvr.ctl; pPrinterPB->readDrvr.ctl = NULL; CallUniversalProc( LMGetJIODone(), uppIODoneProcInfo, pb->usbStatus, clientParam, ctlLocal); } else { IF_DEBUG( DebugStr( kPStrPrinterDriverName"ReadCompletion() pPrinterPB->readDrvr.ctl == NULL" ) ); } MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, "\pUSB PRint, read completion do suspend resume" , pb->usbReference); USBPrintDoSuspendResume(pPrinterPB->interfaceRef); } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: WriteCompletion Input Parameters: pb USB parameter block Output Parameters: <none> Description: USB write requests complete here. We dequeue the MacOS DRVR write requests when breaking up transactions we use ioActCount to determine how much remains of the original request and where the next request begins if we've failed with a USB error usbActCount indicates how much data has been transferred with the current request and if the retry count hasn't been exceeded we proceed as if we'd just requested that much in this transactions note the retry count is cumulative for the original client request not for each usb transaction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void WriteCompletion(USBPB *pb) { // call the user completion routine struct usbPrinterPBStruct *pPrinterPB = (struct usbPrinterPBStruct *) pb->usbRefcon; IOParamPtr clientParam = pPrinterPB->writeDrvr.pb; DCtlPtr ctlLocal; OSStatus err = pb->usbStatus; #if MACSBUG_ON_WRITE_COMPLETE Str255 text; sprintf( (char *)text, " PrinterClass Write Complete(%d): %d", pb->usbStatus, pb->usbActCount ); text[0] = CStrLen((char *)text); // c2pstr DebugStr( text ); #endif // update the amount of data actually transferred clientParam->ioActCount += pb->usbActCount; MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, kPStrPrinterDriverName"WriteCompletion - bytes written " , (UInt32)pb->usbActCount); switch( pb->usbStatus ) { // // only retry low level hardware problems // note: abort transactions will end up here as well // case kUSBLinkErr: case kUSBCRCErr: /* Pipe stall, bad CRC */ case kUSBBitstufErr: /* Pipe stall, bitstuffing */ case kUSBDataToggleErr: /* Pipe stall, Bad data toggle */ case kUSBNotRespondingErr: /* Pipe stall, No device, device hung */ case kUSBPIDCheckErr: /* Pipe stall, PID CRC error */ case kUSBWrongPIDErr: /* Pipe stall, Bad or wrong PID */ if ( --(pPrinterPB->writeRetryCount) >= 0 ) // = is there so we don't retry clearendpoints forever { if(pPrinterPB->writeStatus == noErr) { pPrinterPB->writeStatus = pb->usbStatus; } // we got an error, and we still want to retry, clear any stalls MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, kPStrPrinterDriverName"WriteCompletion retry" , pb->usbStatus ); USBClearPipeStallByReference(pPrinterPB->writePipeRef); pb->usbStatus = noErr; MyUSBExpertStatusLevel(3, pb->usbReference, kPStrPrinterDriverName"**** WriteCompletion clearing endpoint halt *****" , pb->usbStatus ); ClearEndpointHalt(pb); // Sets up PB USBDeviceRequest(pb); return; } break; case kUSBAbortedError: /* user cancel, or hot unplug */ USBClearPipeStallByReference(pPrinterPB->writePipeRef); USBClearPipeStallByReference(pPrinterPB->deviceRef); break; // // any other error will be reported to the client // default: case noErr: break; } if ( (pPrinterPB->writeRetryCount > 0) && (pb->usbStatus == noErr) && (clientParam->ioActCount < clientParam->ioReqCount) ) { // // haven't finish the client's request // update the pointers and start another bulkOut transaction // #if LOCK_MEMORY err = UnlockMemory( pb->usbBuffer, pb->usbReqCount ); IF_DEBUG( if ( err != noErr ) DebugStr( kPStrPrinterDriverName"WriteCompletion UnlockMem failed" ) ); #endif pb->usbBuffer = clientParam->ioBuffer + clientParam->ioActCount; pb->usbReqCount = clientParam->ioReqCount - clientParam->ioActCount; #if MAX_USB_TRANSFER_SIZE if ( pb->usbReqCount > MAX_USB_TRANSFER_SIZE ) pb->usbReqCount = MAX_USB_TRANSFER_SIZE; #endif #if DOUBLE_BUFFER // // make sure we have enough room in our buffer // then copy the user data into our page aligned buffer // if ( pb->usbReqCount > pPrinterPB->pageWriteAlignedBufferSize ) pb->usbReqCount = pPrinterPB->pageWriteAlignedBufferSize; BlockCopy( pb->usbBuffer, pPrinterPB->pageWriteAlignedBuffer, pb->usbReqCount ); pb->usbBuffer = pPrinterPB->pageWriteAlignedBuffer; #endif pPrinterPB->writeStatus = noErr; #if LOCK_MEMORY err = LockMemory( pb->usbBuffer, pb->usbReqCount ); IF_DEBUG( if ( err != noErr ) DebugStr( kPStrPrinterDriverName"WriteCompletion LockMem failed" ) ); if ( err == noErr ) #endif err = SafeUSBBulkWrite( pb ); // Note this is probably hanging off the if above. if ( immediateError(err) ) { MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, kPStrPrinterDriverName"WriteCompletion finish immed err" , err ); pb->usbCompletion = (USBCompletion) NULL; // checked by Finalize if ( pPrinterPB->writeDrvr.ctl != NULL ) { ctlLocal = pPrinterPB->writeDrvr.ctl; pPrinterPB->writeDrvr.ctl = NULL; CallUniversalProc( LMGetJIODone(), uppIODoneProcInfo, err, clientParam, ctlLocal ); } } } else { if(pPrinterPB->writeStatus != noErr) { pb->usbStatus = pPrinterPB->writeStatus; } // // either we have an error which we're not retrying // or we successfully completed // #if LOCK_MEMORY err = UnlockMemory( pb->usbBuffer, pb->usbReqCount ); IF_DEBUG( if ( err != noErr ) DebugStr( kPStrPrinterDriverName"WriteCompletion concluded UnlockMem failed" ) ); #endif IF_DEBUG( if (pb->usbStatus != noErr) MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, kPStrPrinterDriverName"WriteCompletion Error" , pb->usbStatus ) ); pb->usbCompletion = (USBCompletion) NULL; // checked by Finalize MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, kPStrPrinterDriverName"writeDrvr.ctl = " , (UInt32)pPrinterPB->writeDrvr.ctl ); if ( pPrinterPB->writeDrvr.ctl != NULL ) { ctlLocal = pPrinterPB->writeDrvr.ctl; pPrinterPB->writeDrvr.ctl = NULL; CallUniversalProc( LMGetJIODone(), uppIODoneProcInfo, pb->usbStatus, clientParam, ctlLocal ); } else { IF_DEBUG( DebugStr( kPStrPrinterDriverName"WriteCompletion() pPrinterPB->writeDrvr.ctl == NULL" ) ); } MyUSBExpertStatusLevel(kDebugStatusLevel, pb->usbReference, "\pUSB PRint, write completion do suspend resume" , pb->usbReference ); if (gUSBVersion >= kUSBv135) { USBPrintDoSuspendResume(pPrinterPB->interfaceRef); } } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: QueueRead Input Parameters: pb MacOS device manager parameter block ctl device manager dCtl block pPrinterPB current printing device class's storage Output Parameters: <none> Description: MacOS DRVR read requests are re-queued here to the USB Since we only allow one read request pending at a time, we're able to use the USB refCon to point to our class driver's storage. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void QueueRead( IOParamPtr pb, DCtlPtr ctl, struct usbPrinterPBStruct *pPrinterPB ) { OSStatus err; USBPB *usbprint = &pPrinterPB->in; #if MACSBUG_ON_READ Str255 text; sprintf( (char *)text, " PrinterClass Read: %d @ %08x", pb->ioReqCount, pb->ioBuffer ); text[0] = CStrLen((char *)text); // c2pstr MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, text, usbprint->usbStatus ); DebugStr( text ); #endif // BT - 15Jun99, if we're not currently awake, delay this until we are. if(currentlyAre == kUSBPrintSuspended) { pb->ioResult = ioInProgress; MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, "\pUSB Print, delaying read for resume", 0 ); RpPrinterPB = pPrinterPB; Rctl = ctl; Rpb = pb; if( (currentlyAre != kUSBPrintSuspended) && (Rpb != nil) ) { MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, "\pUSB Print, resumed while we wern't looking", 0 ); // resumed and executed this while we were doing this. return; } else { // now wait for resume to happen MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, "\pUSB Print, let resume take over", 0 ); return; } } pb->ioActCount = 0; // nothing transfered yet if ( pPrinterPB->terminating ) pb->ioResult = abortErr; else { pPrinterPB->readRetryCount = 5; pPrinterPB->readStatus = noErr; pPrinterPB->readDrvr.pb= pb; pPrinterPB->readDrvr.ctl = ctl; if (( pPrinterPB->readPipeRef != NULL ) && (pPrinterPB->printerConfigured)) { SetNullUSBParamBlock( pPrinterPB->readPipeRef, usbprint ); MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, kPStrPrinterDriverName"Set up to do bulk read", pb->ioReqCount ); usbprint->usbActCount = 0; usbprint->usbBuffer = pb->ioBuffer; usbprint->usbReqCount = pb->ioReqCount; #if MAX_USB_TRANSFER_SIZE // // the completion routine will queue another BulkWrite until we exhaust the data // or there is an error which can't be retried // if ( usbprint->usbReqCount > MAX_USB_TRANSFER_SIZE ) usbprint->usbReqCount = MAX_USB_TRANSFER_SIZE; #endif #if DOUBLE_BUFFER // // make sure we have enough room in our buffer // then copy the user data into our page aligned buffer // if ( usbprint->usbReqCount > pPrinterPB->pageReadAlignedBufferSize ) usbprint->usbReqCount = pPrinterPB->pageReadAlignedBufferSize; usbprint->usbBuffer = pPrinterPB->pageReadAlignedBuffer; #endif usbprint->usbRefcon = (unsigned long) pPrinterPB; usbprint->usbCompletion = (USBCompletion) ReadCompletion; pb->ioResult = ioInProgress; #if LOCK_MEMORY err = LockMemory( usbprint->usbBuffer, usbprint->usbReqCount ); IF_DEBUG( if ( err != noErr ) DebugStr( kPStrPrinterDriverName"QueueRead LockMem failed" ) ); if ( err == noErr ) #endif err = SafeUSBBulkRead( usbprint ); if ( immediateError(err) ) { MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, kPStrPrinterDriverName"Error doing bulk read",err ); IF_DEBUG( DebugStr( USBStatusStr(err, kPString) ) ); usbprint->usbCompletion = (USBCompletion) NULL; // checked by Finalize pb->ioResult = err; } } else { MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, kPStrPrinterDriverName"Read to unopened pipe" , usbprint->usbStatus ) ; pb->ioResult = abortErr; } } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: QueueWrite Input Parameters: pb MacOS device manager parameter block ctl device manager dCtl block pPrinterPB current printing device class's storage Output Parameters: <none> Description: MacOS DRVR write requests are re-queued here to the USB Since we only allow one read request pending at a time, we're able to use the USB refCon to point to our class driver's storage. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void QueueWrite( IOParamPtr pb, DCtlPtr ctl, struct usbPrinterPBStruct *pPrinterPB ) { OSStatus err; USBPB *usbprint = &pPrinterPB->out; #if VIRTUAL_MEMORY_CHECK // // dump the VM table // Str255 text; LogicalToPhysicalTable *vmTable; MemoryBlock *physical; unsigned long vmCount, vmEntry, vmTblLength; #endif #if MACSBUG_ON_WRITE Str255 text; sprintf( (char *)text, " "kCStrPrinterDriverName"%d @ %08x", pb->ioReqCount, pb->ioBuffer ); text[0] = CStrLen((char *)text); // c2pstr MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, text, usbprint->usbStatus ); DebugStr( text ); #endif // BT - 15Jun99, if we're not currently awake, delay this until we are. if(currentlyAre == kUSBPrintSuspended) { pb->ioResult = ioInProgress; MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, "\pUSB Print, delaying write for resume", 0 ); WpPrinterPB = pPrinterPB; Wctl = ctl; Wpb = pb; if( (currentlyAre != kUSBPrintSuspended) && (Wpb != nil) ) { MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, "\pUSB Print, resumed while we wern't looking", 0 ); // resumed and executed this while we were doing this. } else { // now wait for resume to happen MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, "\pUSB Print, let resume take over", 0 ); return; } } #if VIRTUAL_MEMORY_CHECK // // this check current won't compile without runtime error: need to lock memory, but lock has moved // sprintf( (char *)text, " "kCStrPrinterDriverName"Write: %d @ %08x", pb->ioReqCount, pb->ioBuffer ); text[0] = CStrLen((char *)text); // c2pstr MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, text, usbprint->usbStatus ); vmCount = 127; vmTblLength = (vmCount+1)*sizeof(MemoryBlock); vmTable = (LogicalToPhysicalTable *) NewPtrSys( vmTblLength ); LockMemory( vmTable, vmTblLength ); LockMemory( &vmCount, sizeof(unsigned long) ); vmTable->logical.address = pb->ioBuffer; vmTable->logical.count = pb->ioReqCount; err = GetPhysical( vmTable, &vmCount ); if ( err == noErr ) { for ( vmEntry = 0, physical = &vmTable->physical[0]; vmEntry < vmCount; ++vmEntry , ++physical) { sprintf( (char *)text, " "kCStrPrinterDriverName"Write: VM @%08x (%d)", physical->address, physical->count ); text[0] = CStrLen((char *)text); // c2pstr MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, text, usbprint->usbStatus ); } } UnlockMemory( vmTable, vmTblLength ); UnlockMemory( &vmCount, sizeof(unsigned long) ); #endif pb->ioActCount = 0; // nothing transfered yet usbprint->usbStatus = noErr; // forget about abort and things from previous writes if ( pPrinterPB->terminating ) pb->ioResult = abortErr; else { pPrinterPB->writeRetryCount = 5; pPrinterPB->writeStatus = noErr; pPrinterPB->writeDrvr.pb = pb; pPrinterPB->writeDrvr.ctl = ctl; if (( pPrinterPB->writePipeRef != NULL ) && (pPrinterPB->printerConfigured)) { SetNullUSBParamBlock( pPrinterPB->writePipeRef, usbprint ); MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, kPStrPrinterDriverName"Set up to do bulk write", pb->ioReqCount ); usbprint->usbActCount = 0; usbprint->usbBuffer = pb->ioBuffer; usbprint->usbRefcon = (unsigned long) pPrinterPB; usbprint->usbCompletion = (USBCompletion) WriteCompletion; #if MAX_USB_TRANSFER_SIZE // // the completion routine will queue another BulkWrite until we exhaust the data // or there is an error which can't be retried // if ( pb->ioReqCount > MAX_USB_TRANSFER_SIZE ) usbprint->usbReqCount = MAX_USB_TRANSFER_SIZE; else #endif usbprint->usbReqCount = pb->ioReqCount; #if DOUBLE_BUFFER // // make sure we have enough room in our buffer // then copy the user data into our page aligned buffer // if ( usbprint->usbReqCount > pPrinterPB->pageWriteAlignedBufferSize ) usbprint->usbReqCount = pPrinterPB->pageWriteAlignedBufferSize; BlockCopy( usbprint->usbBuffer, pPrinterPB->pageWriteAlignedBuffer, usbprint->usbReqCount ); usbprint->usbBuffer = pPrinterPB->pageWriteAlignedBuffer; #endif pb->ioResult = ioInProgress; #if LOCK_MEMORY err = LockMemory( usbprint->usbBuffer, usbprint->usbReqCount ); IF_DEBUG( if ( err != noErr ) DebugStr( kPStrPrinterDriverName"QueueWrite LockMem failed" ) ); if ( err == noErr ) #endif err = SafeUSBBulkWrite( usbprint ); if ( immediateError(err) ) { MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, kPStrPrinterDriverName"Immediate error doing bulk write",0 ); IF_DEBUG( DebugStr( USBStatusStr(err, kPString) ) ); usbprint->usbCompletion = (USBCompletion) NULL; // checked by Finalize pb->ioResult = err; } // // note our logging is one write for each the client request // not one write for each usb transaction // LOGGING( fwrite( pb->ioBuffer, sizeof(char), pb->ioReqCount, logfile ) ); } else { MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, kPStrPrinterDriverName"Write to unopened pipe" , usbprint->usbStatus ); pb->ioResult = abortErr; } } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: Abort Input Parameters: refNum DRVR refnum pPrinterPB current printing device class's storage Output Parameters: Description: Asynchronous completion. Note: USBAbortPipeByReference can leave the data toggle in the wrong state. We need to abort both pipes, and then the client must soft reset the printer to get the printer's data toggles correct. We prematurely call completion routines for active i/o so that CloseDriverSync will not hang the system after an abort, including hot unplug. This works out okay, since the request will be dequeued now, and when the i/o actually terminates the system will be called with an invalid queue element and then reject this second attempt to dequeue the i/o. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ OSStatus Abort( DriverRefNum refNum, struct usbPrinterPBStruct *pPrinterPB ) { OSStatus err = noErr; // // if there's any pending io this will call the completion routine // MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, "\pUSB Print - Abort called" , 0); if (refNum == pPrinterPB->outRefNum) { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, "\pUSB Print - Abort called on out pipe" , 0); if ( pPrinterPB->in.usbCompletion != (USBCompletion) NULL ) MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName "***** Abort with write in progress" , 0); err = USBAbortPipeByReference( pPrinterPB->writePipeRef ); err = USBClearPipeStallByReference( pPrinterPB->writePipeRef ); if ( pPrinterPB->out.usbCompletion != (USBCompletion) NULL ) CallUniversalProc( LMGetJIODone(), uppIODoneProcInfo, abortErr, pPrinterPB->writeDrvr.pb, pPrinterPB->writeDrvr.ctl ); } if (refNum == pPrinterPB->inRefNum) { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, "\pUSB Print - Abort called on in pipe" , 0); if ( pPrinterPB->out.usbCompletion != (USBCompletion) NULL ) MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName "***** Abort with read in progress" , 0); err = USBAbortPipeByReference( pPrinterPB->readPipeRef ); err = USBClearPipeStallByReference( pPrinterPB->readPipeRef ); if ( pPrinterPB->in.usbCompletion != (USBCompletion) NULL ) CallUniversalProc( LMGetJIODone(), uppIODoneProcInfo, abortErr, pPrinterPB->readDrvr.pb, pPrinterPB->readDrvr.ctl ); } return err; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: CompletionProc Input Parameters: pb USB param block ptr Output Parameters: Description: Asynchronous completion routine for DRVR requests. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void CompletionProc(USBPB *pb) { // call the user completion routine struct usbPrinterPBStruct *pPrinterPB = (struct usbPrinterPBStruct *) pb->usbRefcon; IOParamPtr clientParam = pPrinterPB->statusDrvr.pb; clientParam->ioActCount = pb->usbActCount; if ( pb->usbStatus != noErr ) { USBClearPipeStallByReference(pb->usbReference); // we got an error, try to clear any stalls IF_DEBUG( MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName "CompletionProc Error" , pb->usbStatus ) ); IF_DEBUG( MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, USBStatusStr(pb->usbStatus, kPString) , pb->usbStatus ) ); } pb->usbCompletion = (USBCompletion) NULL; // checked by Finalize if ( pPrinterPB->statusDrvr.ctl != NULL ) CallUniversalProc( LMGetJIODone(), uppIODoneProcInfo, pb->usbStatus, clientParam, pPrinterPB->statusDrvr.ctl ); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: CentronicsStatus Input Parameters: pb MacOS device manager parameter block ctl device manager dCtl block pPrinterPB current printing device class's storage Output Parameters: pStatusByte Description: setup the device request for a USB printer class request one byte status. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void CentronicsStatus( USBPB *usbprint, Ptr buffer, short interfaceNumber ) { usbprint->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBIn, kUSBClass, kUSBInterface); usbprint->usb.cntl.BRequest = kUSBPrintClassGetCentronicsStatus; usbprint->usb.cntl.WValue = 0; usbprint->usb.cntl.WIndex = interfaceNumber; usbprint->usbReqCount = 1; usbprint->usbBuffer = buffer; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: ClearEndpointHalt Input Parameters: usbprint USB param block Output Parameters: Description: Setup the device request for a USB clear endpoint halt. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void ClearEndpointHalt( USBPB *usbprint) { usbprint->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBStandard, kUSBEndpoint); usbprint->usb.cntl.BRequest = kUSBRqClearFeature; usbprint->usb.cntl.WValue = kUSBFeatureEndpointStall; usbprint->usbFlags = kUSBAddressRequest; usbprint->usbReqCount = 0; usbprint->usbBuffer = NULL; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: SoftReset Input Parameters: usbprint USB param block interfaceNumber Output Parameters: Description: Setup the device request for a USB printer class request soft reset. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void SoftReset( USBPB *usbprint, short interfaceNumber ) { usbprint->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBClass, kUSBOther); usbprint->usb.cntl.BRequest = kUSBPrintClassSoftReset; usbprint->usb.cntl.WValue = 0; usbprint->usb.cntl.WIndex = interfaceNumber; usbprint->usbReqCount = 0; usbprint->usbBuffer = NULL; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: CapabilityRequest Input Parameters: usbprint USB parameter block p result pointer length amount of data allocated config interfaceNum alternateSetting Output Parameters: p result pointer length amount of data allocated Description: setup the device request for a USB printer class request 1284 id string. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void CapabilityRequest( USBPB *pb, Ptr p, long length, short configValue, short interfaceNumber, short alternateSetting ) { pb->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBIn, kUSBClass, kUSBInterface); pb->usb.cntl.BRequest = kUSBPrintClassGetDeviceID; pb->usb.cntl.WValue = configValue; // configuration pb->usb.cntl.WIndex = (interfaceNumber<<8) | alternateSetting; pb->usbReqCount = length; pb->usbBuffer = p; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: StatusControlRequests Input Parameters: pb MacOS device manager parameter block ctl device manager dCtl block pPrinterPB current printing device class's storage Output Parameters: Description: Asynchronous completion. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void ControlStatusRequests( IOParamPtr pb, DCtlPtr ctl, struct usbPrinterPBStruct *pPrinterPB ) { // // queue a transaction to retrieve the centronics status // OSStatus err; USBPB *usbprint = &pPrinterPB->pb; Boolean dosomething = false; #if DEBUG Str255 text; sprintf( (char *)text, " PrinterClass ControlStatus: %d", ((CntrlParam *) pb)->csCode ); text[0] = CStrLen((char *)text); // c2pstr MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, text, usbprint->usbStatus ); #endif if (pPrinterPB->terminating) { pb->ioResult = abortErr; } else { switch( ((CntrlParam *) pb)->csCode ) { case kDrvrCentronicsStatus: dosomething = true; CentronicsStatus( usbprint, *((Ptr *)((CntrlParam *) pb)->csParam), pPrinterPB->interfaceNumber ); SetNullUSBParamBlock(pPrinterPB->deviceRef, usbprint ); break; case kDrvr1284IdString: dosomething = true; CapabilityRequest( usbprint, *((Ptr *)((CntrlParam *) pb)->csParam), *((long *) &((CntrlParam *) pb)->csParam[4]), 0, // configuration pPrinterPB->interfaceNumber, pPrinterPB->alternateSetting); SetNullUSBParamBlock(pPrinterPB->deviceRef, usbprint ); break; case kDrvrSoftReset: dosomething = true; if ( ((CntrlParam *) pb)->ioCRefNum == pPrinterPB->outRefNum ) { SetNullUSBParamBlock(pPrinterPB->writePipeRef, usbprint ); } else { SetNullUSBParamBlock(pPrinterPB->readPipeRef, usbprint ); } ClearEndpointHalt( usbprint ); break; case kDrvrPrivateLog: // BT - 15Jun99, print a message in the log for the 68k code. MyUSBExpertStatusLevel(((CntrlParam *) pb)->csParam[2], pPrinterPB->deviceRef, *(unsigned char **)(((CntrlParam *) pb)->csParam), *((long *)&((CntrlParam *) pb)->csParam[4]) ); return; break; case kDrvrPrivateOpnClose: if (gUSBVersion >= kUSBv135) { // BT - 15Jun99, Open or close has been called, arrange for suspend or resume. MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, "\pUSB print driver kDrvrPrivateOpnClose", ((CntrlParam *) pb)->csParam[0] == 0); // If csParam[0] is 1 we're being opened, if 0 we're being closed if(((CntrlParam *) pb)->csParam[0] == 1) { openRef++; wantToBe = kUSBPrintResumed; // We're being opened } else if(((CntrlParam *) pb)->csParam[0] == 0) { if (--openRef < 0) openRef = 0; if (!openRef) // is this the last close? wantToBe = kUSBPrintSuspended; else wantToBe = kUSBPrintResumed; } else { MyUSBExpertStatusLevel(2, pPrinterPB->deviceRef, "\pUSB print driver kDrvrPrivateOpnClose unknown message", ((CntrlParam *) pb)->csParam[0] == 0); } USBPrintDoSuspendResume(pPrinterPB->interfaceRef); } return; break; default: break; } if ( !dosomething ) pb->ioResult = paramErr; else { // BT - 15Jun99, if we're not currently awake, delay this until we are. if(currentlyAre == kUSBPrintSuspended ) { if (Cpb != nil){ pb->ioResult = notOpenErr; MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, "\pUSB Print, Can't support more than one delayed control call while suspended!", pb->ioResult ); return; } pb->ioResult = ioInProgress; MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, "\pUSB Print, delaying control for resume", 0 ); CpPrinterPB = pPrinterPB; Cctl = ctl; Cpb = pb; if( (currentlyAre != kUSBPrintSuspended) && (Rpb != nil) ) { MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, "\pUSB Print, resumed while we wern't looking", 0 ); // resumed and executed this while we were doing this. return; } else { // now wait for resume to happen MyUSBExpertStatusLevel(kDebugStatusLevel, usbprint->usbReference, "\pUSB Print, let resume take over", 0 ); return; } } usbprint->usbActCount = 0; usbprint->usbCompletion = (USBCompletion)CompletionProc; usbprint->usbRefcon = (unsigned long) pPrinterPB; pb->ioResult = ioInProgress; pPrinterPB->statusDrvr.pb = pb; pPrinterPB->statusDrvr.ctl = ctl; err = USBDeviceRequest(usbprint); if(immediateError(err)) { USBExpertFatalError(usbprint->usbReference, err, kPStrPrinterDriverName"StatusControlRequests", 0); usbprint->usbCompletion = (USBCompletion) NULL; // checked by Finalize pb->ioResult = err; } } } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: PrinterDeviceCompletionProc Input Parameters: pb refCon tells which state we're completing Output Parameters: <none> Description: Complete asynch transactions initiated by PrinterDeviceInitiateTransaction. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ static void PrinterDeviceCompletionProc(USBPB *pb) { Str255 tempstr1,tempstr2; register struct usbPrinterPBStruct *pPrinterPB; OSStatus err; UInt32 i = 0; pPrinterPB = (struct usbPrinterPBStruct *)(pb); pPrinterPB->transDepth--; if ((pPrinterPB->transDepth < 0) || (pPrinterPB->transDepth > 1)) { USBExpertFatalError(pPrinterPB->deviceRef, kUSBInternalErr, kPStrPrinterDriverName "CompletionProc Illegal Transaction Depth", pPrinterPB->transDepth ); } MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, StateStr(pPrinterPB->pb.usbRefcon, kPString) , pPrinterPB->pb.usbStatus ); pPrinterPB->delayInProgress = false; if ( pPrinterPB->terminating ) { // if we've been hot unplugged // don't startup any new transactions // allow PrintDriverFinalize to continue pPrinterPB->pb.usbStatus = kUSBAbortedError; pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } switch( pPrinterPB->pb.usbStatus ) { case kUSBPending: case noErr: pPrinterPB->pb.usbRefcon &= ~kRetryTransaction; pPrinterPB->retryCount = kPrinterRetryCount; break; case kUSBLinkErr: case kUSBCRCErr: /* Pipe stall, bad CRC */ case kUSBBitstufErr: /* Pipe stall, bitstuffing */ case kUSBDataToggleErr: /* Pipe stall, Bad data toggle */ case kUSBNotRespondingErr: /* Pipe stall, No device, device hung */ case kUSBPIDCheckErr: /* Pipe stall, PID CRC error */ case kUSBWrongPIDErr: /* Pipe stall, Bad or wrong PID */ case kUSBTimedOut: /* Transaction timed out. */ default: USBClearPipeStallByReference(pPrinterPB->deviceRef); // we got an error, try to clear any stalls MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName " retry", pPrinterPB->pb.usbStatus); // clear out the transaction pending flag pPrinterPB->pb.usbRefcon &= ~(kTransactionPending + kReturnFromDriver); pPrinterPB->pb.usbRefcon |= kRetryTransaction; pPrinterPB->retryCount--; if (!pPrinterPB->retryCount) { USBExpertFatalError(pPrinterPB->deviceRef, kUSBInternalErr, kPStrPrinterDriverName "Retry failed", pPrinterPB->pb.usbRefcon & ~kRetryTransaction); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } else { pPrinterPB->pb.usbStatus = noErr; // let's retry one more time } break; case kUSBNotFound: break; case kUSBAbortedError: /* user cancel, or hot unplug */ // clear out the transaction pending flag pPrinterPB->pb.usbCompletion = (USBCompletion) NULL; pPrinterPB->pb.usbRefcon &= ~(kTransactionPending + kReturnFromDriver); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; break; } if (pPrinterPB->pb.usbRefcon & kTransactionPending) { short length; // // advance to the next state // pPrinterPB->pb.usbRefcon &= ~(kTransactionPending + kReturnFromDriver); switch(pPrinterPB->pb.usbRefcon) { case kFindInterface_bidirectional: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"kFindInterface_bidirectional completed", pPrinterPB->pb.usb.cntl.WIndex); if ( pPrinterPB->pb.usbStatus == noErr ) { if ((pPrinterPB->pb.usbClassType == kUSBPrintingClass) && (pPrinterPB->pb.usbSubclass == kUSBPrinterSubclass) && (pPrinterPB->pb.usbProtocol == kUSBPrinterBidirectionalProtocol)) { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Found bidirectional interface at alt setting ", pPrinterPB->pb.usbOther); pPrinterPB->configurationNumber = pPrinterPB->pb.usb.cntl.WValue; pPrinterPB->alternateSetting = pPrinterPB->pb.usbOther; pPrinterPB->printerProtocol = kUSBPrinterBidirectionalProtocol; // if we started out as a device class driver, then the interface descriptor will be NULL // this would mean that the device will need to be opened before anything can be done with it // if the interface descriptor point isn't null, then we started as an interface driver, // so just skip by the opendevice call if (pPrinterPB->pInterfaceDescriptor != NULL) { // did we find the interface we were looking for? if (pPrinterPB->interfaceNumber == pPrinterPB->pb.usb.cntl.WIndex) { pPrinterPB->pb.usbRefcon = kSetInterface; } else { USBExpertFatalError(pPrinterPB->deviceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kFindInterface_Bidirectional didn't find the right interface", 0); } } else { pPrinterPB->pb.usbRefcon = kOpenDevice; pPrinterPB->interfaceNumber = pPrinterPB->pb.usb.cntl.WIndex; } } } else { if ( pPrinterPB->pb.usbStatus == kUSBNotFound ) { MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Bidirectional interface was not found", pPrinterPB->pb.usb.cntl.WIndex); pPrinterPB->pb.usbRefcon = kFindInterface_unidirectional; pPrinterPB->pb.usbStatus = noErr; // must clear error for state machine to continue <USB46> } else { USBExpertFatalError(pPrinterPB->deviceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kFindInterface_bidirectional failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } } break; case kFindInterface_unidirectional: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"kFindInterface_unidirectional completed", pPrinterPB->pb.usb.cntl.WIndex); if ( pPrinterPB->pb.usbStatus == noErr ) { if ((pPrinterPB->pb.usbClassType == kUSBPrintingClass) && (pPrinterPB->pb.usbSubclass == kUSBPrinterSubclass) && (pPrinterPB->pb.usbProtocol == kUSBPrinterUnidirectionalProtocol)) { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Found unidirection interface at alt setting ", pPrinterPB->pb.usbOther); pPrinterPB->pb.usbRefcon = kOpenDevice; pPrinterPB->configurationNumber = pPrinterPB->pb.usb.cntl.WValue; pPrinterPB->alternateSetting = pPrinterPB->pb.usbOther; pPrinterPB->printerProtocol = kUSBPrinterUnidirectionalProtocol; // if we started out as a device class driver, then the interface descriptor will be NULL // this would mean that the device will need to be opened before anything can be done with it // if the interface descriptor point isn't null, then we started as an interface driver, // so just skip by the opendevice call if (pPrinterPB->pInterfaceDescriptor) { if (pPrinterPB->interfaceNumber == pPrinterPB->pb.usb.cntl.WIndex) { pPrinterPB->pb.usbRefcon = kSetInterface; } else { USBExpertFatalError(pPrinterPB->deviceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kFindInterface_Unidirectional didn't find the right interface", 0); } } else { pPrinterPB->pb.usbRefcon = kOpenDevice; pPrinterPB->interfaceNumber = pPrinterPB->pb.usb.cntl.WIndex; } } } else { if ( pPrinterPB->pb.usbStatus == kUSBNotFound ) { MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"Unidirectional interface was not found", pPrinterPB->pb.usb.cntl.WIndex); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } else { USBExpertFatalError(pPrinterPB->deviceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kFindInterface_unidirectional failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } } break; case kOpenDevice: if ( pPrinterPB->pb.usbStatus == noErr ) { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"kOpenDevice completed", pPrinterPB->pb.usbStatus); pPrinterPB->pb.usbRefcon = kNewInterfaceRef; } else { USBExpertFatalError(pPrinterPB->deviceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kOpenDevice failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kNewInterfaceRef: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"kNewInterfaceRef completed", pPrinterPB->pb.usbReference); if ( pPrinterPB->pb.usbStatus == noErr ) { pPrinterPB->interfaceRef = pPrinterPB->pb.usbReference; pPrinterPB->pb.usbRefcon = kSetInterface; } else { USBExpertFatalError(pPrinterPB->deviceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kNewInterfaceRef failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kSetInterface: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kSetInterface completed", 0); if ( pPrinterPB->pb.usbStatus == noErr ) { pPrinterPB->pb.usbRefcon = kConfigureInterface; } else { USBExpertFatalError(pPrinterPB->deviceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kSetInterface failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kConfigureInterface: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kConfigureInterface completed, pipes = ", pPrinterPB->pb.usbOther); if ( pPrinterPB->pb.usbStatus == noErr ) { pPrinterPB->pipeCount = pPrinterPB->pb.usbOther; pPrinterPB->pb.usbRefcon = kGetCapabilityString; } else { USBExpertFatalError(pPrinterPB->deviceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kConfigureInterface failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kGetCapabilityString: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kGetCapabilityString completed", pPrinterPB->pb.usbStatus); if ( pPrinterPB->pb.usbActCount >= 2 ) { length = pPrinterPB->pCapabilityString[1] | (pPrinterPB->pCapabilityString[0] << 8); } else { length = 0; } if ( pPrinterPB->pb.usbStatus == noErr ) { // // In the short term (fall '98) several vendors are planning on shipping // devices with a parallel port and using the Lucent USS-720 USB-to-parallel hardware. // Unfortunately this isn't an ideal solution from the USB perspective: // users can leave the printer off while the cable responds that there's // a printer connected. Then we have no way of using the DEVICE_ID // string to tag the printer and register it. // To alleviate this situation, we repeatedly poll the USS-720 if the DEVICE_ID // string is null. Every few seconds we'll retry this state. // At some point the user wants to print and switches on the printer. The class // driver then picks up from here and registers the device properly. // if ( pPrinterPB->pb.usbActCount == 0 ) pPrinterPB->pb.usbRefcon = kDelayGetCapability; else pPrinterPB->pb.usbRefcon = kFindBulkOutPipe; } else { if ( pPrinterPB->pb.usbStatus == kUSBOverRunErr ) { // // if we've haven't managed to read the whole capability string // we need to allocate memory and read it in by initiating kGetFullCapabilityString // if ( length > sizeof(pPrinterPB->capability) && pPrinterPB->pb.usbRefcon == kGetCapabilityString ) { pPrinterPB->pb.usbRefcon = kAllocateCapabilityMem; pPrinterPB->pb.usbStatus = noErr; // must clear error for state machine to continue <USB46> break; // advance to that state <USB46> } } else { USBExpertFatalError(pPrinterPB->interfaceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kGetCapabilityString failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } } // write the capability string to the log if ((pPrinterPB->pb.usbStatus == kUSBOverRunErr) || (pPrinterPB->pb.usbStatus == noErr)) { if (length > 2) // is there a valid string? (length more than 2 bytes) { length -= 2; if (length > 200) // cut the string off at 200 characters length = 200; BlockMove( (Ptr)&(pPrinterPB->pCapabilityString[2]), (Ptr)tempstr2, length); tempstr2[length] = '\0'; // mark the end of the cstring sprintf( (char *)tempstr1, (char const *) kCStrPrinterDriverName"1284 Capability String: %s", tempstr2 ); CStrToPStr( (unsigned char *)tempstr2, (char *)tempstr1); // convert it to a pstring } else { CStrToPStr( (unsigned char *)tempstr2, (char *) kCStrPrinterDriverName"Capability string is empty!!" ); } MyUSBExpertStatusLevel(kNormalStatusLevel, pPrinterPB->deviceRef, tempstr2, length); } break; case kDelayGetCapability: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kDelayGetCapability completed", pPrinterPB->pb.usbStatus); if ( pPrinterPB->pb.usbStatus == noErr ) { pPrinterPB->pb.usbRefcon = kGetCapabilityString; } else { USBExpertFatalError(pPrinterPB->interfaceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kDelayGetCapability failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kAllocateCapabilityMem: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kAllocateCapabilityMem completed", pPrinterPB->pb.usbStatus); if ( pPrinterPB->pb.usbStatus == noErr ) { pPrinterPB->pCapabilityString = pPrinterPB->pb.usbBuffer; pPrinterPB->pb.usbRefcon = kGetFullCapabilityString; } else { USBExpertFatalError(pPrinterPB->interfaceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kAllocateCapabilityMem failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kGetFullCapabilityString: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kGetFullCapabilityString completed", pPrinterPB->pb.usbStatus); if ( pPrinterPB->pb.usbStatus == noErr ) { pPrinterPB->pb.usbRefcon = kGetInterface; } else { USBExpertFatalError(pPrinterPB->interfaceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kGetFullCapabilityString failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kGetInterface: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kGetInterface completed", pPrinterPB->pb.usbStatus); if (( pPrinterPB->pb.usbStatus == noErr ) && (pPrinterPB->whichAltInterface == pPrinterPB->alternateSetting)) { pPrinterPB->pb.usbRefcon = kFindBulkOutPipe; } else { USBExpertFatalError(pPrinterPB->interfaceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kGetInterface - wrong interface selected", pPrinterPB->whichAltInterface); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kFindBulkOutPipe: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kFindBulkOutPipe completed", pPrinterPB->pb.usbReference); if ( pPrinterPB->pb.usbStatus == noErr ) { pPrinterPB->writePipeRef = pPrinterPB->pb.usbReference; // remember the ref pPrinterPB->out = pPrinterPB->pb; // copy the paramblock pPrinterPB->out.usbCompletion = (USBCompletion) NULL; // for finalize if ( pPrinterPB->printerProtocol == kUSBPrinterBidirectionalProtocol ) pPrinterPB->pb.usbRefcon = kFindBulkInPipe; else pPrinterPB->pb.usbRefcon = kTaskTimeRequired; } else { USBExpertFatalError(pPrinterPB->interfaceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kFindBulkOutPipe failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kFindBulkInPipe: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kFindBulkInPipe completed", pPrinterPB->pb.usbReference); if ( pPrinterPB->pb.usbStatus == noErr ) { pPrinterPB->readPipeRef = pPrinterPB->pb.usbReference; pPrinterPB->in = pPrinterPB->pb; // copy the paramblock pPrinterPB->in.usbCompletion = (USBCompletion) NULL; // for finalize pPrinterPB->pb.usbRefcon = kTaskTimeRequired; } else { USBExpertFatalError(pPrinterPB->interfaceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"kFindBulkInPipe failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kTaskTimeRequired: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kTaskTimeRequired completed (now at task time)", pPrinterPB->pb.usbReference); // // once we know what device we're dealing with // open the i/o channel(s) to the device // and enter it in the name registry // err = InstallDrivers( pPrinterPB ); if ( err == noErr ) { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kTaskTimeRequired - drivers installed", pPrinterPB->pb.usbReference); err = RegisterDevice( pPrinterPB ); if ( err == noErr ) { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kTaskTimeRequired - RegisterDevice successful", pPrinterPB->pb.usbReference); } else { USBExpertFatalError(pPrinterPB->interfaceRef, err, kPStrPrinterDriverName"kTaskTimeRequired - RegisterDevice failed", 0); } } else { USBExpertFatalError(pPrinterPB->interfaceRef, err, kPStrPrinterDriverName"kTaskTimeRequired - InstallDrivers failed", 0); } pPrinterPB->pb.usbCompletion = (USBCompletion) NULL; // Finalize if ( pPrinterPB->pb.usbStatus == noErr ) { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, "\pPrinter driver now exiting setup, calling suspend", pPrinterPB->interfaceRef ); USBPrintDoSuspendResume(pPrinterPB->interfaceRef); pPrinterPB->pb.usbRefcon = kReturnFromDriver; pPrinterPB->printerConfigured = true; } else { USBExpertFatalError(pPrinterPB->interfaceRef, pPrinterPB->pb.usbStatus, kPStrPrinterDriverName"RegisterDevice failed", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kGetCentronicsStatus: // // if InitiateTransaction fell through on it's kTaskTimeRequired case we'll end up here // if ( pPrinterPB->pb.usbStatus == noErr ) { unsigned char text[255]; sprintf( (char *)text, " Centronics Status: 0x%02x", pPrinterPB->centronics.b ); text[0] = CStrLen((char *)text); // c2pstr MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, text, pb->usbStatus ); #if DEBUG if ( !pPrinterPB->centronics.status.notError ) { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"Error at printer", pPrinterPB->pb.usbStatus); } if ( pPrinterPB->centronics.status.paperError ) { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"Check paper", pPrinterPB->pb.usbStatus); } if ( !pPrinterPB->centronics.status.select ) { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"printer offline", pPrinterPB->pb.usbStatus); } #endif pPrinterPB->pb.usbRefcon = kDelayGetCentronicsStatus; } else { MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kGetCentronicsStatus - error getting centronics status", pPrinterPB->pb.usbStatus); } break; case kDelayGetCentronicsStatus: // // loop around continually getting status // if ( pPrinterPB->pb.usbStatus == noErr ) { pPrinterPB->pb.usbRefcon = kGetCentronicsStatus; } break; case kNilCompletion: default: if ( pPrinterPB->pb.usbStatus == noErr ) pPrinterPB->pb.usbRefcon = kUndefined | kReturnFromDriver; break; } } // did the removal notification get called? If so, don't start another transaction. Just exit if (!pPrinterPB->terminating) { if (pPrinterPB->pb.usbStatus == noErr ) { if (!(pPrinterPB->pb.usbRefcon & kReturnFromDriver)) PrinterDeviceInitiateTransaction(pb); } else if ( pPrinterPB->pb.usbRefcon & kReturnFromDriver) { USBExpertFatalError(pPrinterPB->deviceRef, pPrinterPB->pb.usbStatus, StateStr(pPrinterPB->pb.usbRefcon, kPString), pPrinterPB->pb.usbRefcon); USBExpertFatalError(pPrinterPB->deviceRef, pPrinterPB->pb.usbStatus, USBStatusStr(pPrinterPB->pb.usbStatus, kPString), 0); } } else { pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } // If we're exiting the driver, then make certain the completion routine is set to NULL // this lets the driver removal task know that there's nothing pending. if ( pPrinterPB->pb.usbRefcon & kReturnFromDriver) { pPrinterPB->pb.usbCompletion = (USBCompletion) NULL; } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: PrinterDeviceInitiateTransaction Input Parameters: pb USB parameter block Output Parameters: Description: Since USB transactions are asynchronous we use the refCon field in the parameter block to implement the following logic via a state machine. Start out by getting the device configuration descriptor If the device has more than one printing interface If a bidirectional interface exists select it Else select the (mandatory) unidirectional interface Get the (mandatory) 1284 capability string Open pipes to the BulkOut (and optional BulkIn) endpoints Install read and write drivers in the unit table Using information from the capability string enter the printer in the MacOS name registry. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void PrinterDeviceInitiateTransaction(USBPB *pb) { register struct usbPrinterPBStruct *pPrinterPB; UInt16 length; OSStatus err; pPrinterPB = (struct usbPrinterPBStruct *)(pb); pPrinterPB->transDepth++; if ((pPrinterPB->transDepth < 0) || (pPrinterPB->transDepth > 1)) { USBExpertFatalError(pPrinterPB->deviceRef, kUSBInternalErr, kPStrPrinterDriverName"InitiateTransaction illegal transaction depth", 0); } IF_DEBUG( MyUSBExpertStatusLevel(5, pPrinterPB->deviceRef, StateStr(pPrinterPB->pb.usbRefcon, kPString), 0) ); pPrinterPB->delayInProgress = false; switch(pPrinterPB->pb.usbRefcon & ~kRetryTransaction) { case kFindInterface_bidirectional: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"kFindInterface_bidirectional", 0); SetNullUSBParamBlock( pPrinterPB->deviceRef, &pPrinterPB->pb ); pPrinterPB->pb.usbBuffer = 0; pPrinterPB->pb.usbActCount = 0; pPrinterPB->pb.usbReqCount = 0; pPrinterPB->pb.usb.cntl.WIndex = 0; pPrinterPB->pb.usb.cntl.WValue = 0; pPrinterPB->pb.usbClassType = kUSBPrintingClass; pPrinterPB->pb.usbSubclass = kUSBPrinterSubclass; pPrinterPB->pb.usbProtocol = kUSBPrinterBidirectionalProtocol; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; err = USBFindNextInterface(pb); if(immediateError(err)) { USBExpertFatalError(pPrinterPB->pb.usbReference, kUSBInternalErr, kPStrPrinterDriverName"kFindInterface_bidirectional - immediate error", err); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kFindInterface_unidirectional: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"kFindInterface_unidirectional", 0); SetNullUSBParamBlock( pPrinterPB->deviceRef, &pPrinterPB->pb ); pPrinterPB->pb.usbBuffer = 0; pPrinterPB->pb.usbActCount = 0; pPrinterPB->pb.usbReqCount = 0; pPrinterPB->pb.usb.cntl.WIndex = 0; pPrinterPB->pb.usb.cntl.WValue = 0; pPrinterPB->pb.usbClassType = kUSBPrintingClass; pPrinterPB->pb.usbSubclass = kUSBPrinterSubclass; pPrinterPB->pb.usbProtocol = kUSBPrinterUnidirectionalProtocol; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; err = USBFindNextInterface(pb); if(immediateError(err)) { USBExpertFatalError(pPrinterPB->pb.usbReference, kUSBInternalErr, kPStrPrinterDriverName"kFindInterface_unidirectional - immediate error", err); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kOpenDevice: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"kOpenDevice", 0); SetNullUSBParamBlock( pPrinterPB->deviceRef, &pPrinterPB->pb ); pPrinterPB->pb.usbBuffer = 0; pPrinterPB->pb.usbActCount = 0; pPrinterPB->pb.usbReqCount = 0; pPrinterPB->pb.usb.cntl.WValue = pPrinterPB->configurationNumber; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; err = USBOpenDevice(pb); if(immediateError(err)) { USBExpertFatalError(pPrinterPB->pb.usbReference, kUSBInternalErr, kPStrPrinterDriverName"USBOpenDevice - immediate error", err); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kNewInterfaceRef: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->deviceRef, kPStrPrinterDriverName"kNewInterfaceRef for interface number", pPrinterPB->interfaceNumber); SetNullUSBParamBlock( pPrinterPB->deviceRef, &pPrinterPB->pb ); pPrinterPB->pb.usbBuffer = 0; pPrinterPB->pb.usbActCount = 0; pPrinterPB->pb.usbReqCount = 0; pPrinterPB->pb.usb.cntl.WIndex = pPrinterPB->interfaceNumber; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; err = USBNewInterfaceRef(pb); if(immediateError(err)) { USBExpertFatalError(pPrinterPB->pb.usbReference, kUSBInternalErr, kPStrPrinterDriverName"USBNewInterfaceRef - immediate error", err); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kSetInterface: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kSetInterface to alternate setting", pPrinterPB->alternateSetting); SetNullUSBParamBlock( pPrinterPB->interfaceRef, &pPrinterPB->pb ); pPrinterPB->pb.usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBStandard, kUSBInterface); pPrinterPB->pb.usb.cntl.BRequest = kUSBRqSetInterface; pPrinterPB->pb.usb.cntl.WValue = pPrinterPB->alternateSetting; // alternate setting pPrinterPB->pb.usb.cntl.WIndex = pPrinterPB->interfaceNumber; // interface pPrinterPB->pb.usbReqCount = 0; pPrinterPB->pb.usbBuffer = NULL; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; err = USBDeviceRequest(pb); if(immediateError(err)) { USBExpertFatalError(pPrinterPB->pb.usbReference, kUSBInternalErr, kPStrPrinterDriverName"kSetInterface - immediate error", err); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kConfigureInterface: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kConfigureInterface for alternate setting", pPrinterPB->alternateSetting); SetNullUSBParamBlock( pPrinterPB->interfaceRef, &pPrinterPB->pb ); pPrinterPB->pb.usbBuffer = 0; pPrinterPB->pb.usbActCount = 0; pPrinterPB->pb.usbReqCount = 0; pPrinterPB->pb.usbOther = pPrinterPB->alternateSetting; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; err = USBConfigureInterface(pb); if(immediateError(err)) { USBExpertFatalError(pPrinterPB->pb.usbReference, kUSBInternalErr, kPStrPrinterDriverName"USBConfigureInterface - immediate error)", err); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kGetCapabilityString: // // once the interface (and alternate) is assinged // we can retreive the 1284 capability string to see what kind of printer // is attached MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kGetCapabilityString", 0); pPrinterPB->pCapabilityString = pPrinterPB->capability; GetCapability( pPrinterPB, pPrinterPB->pCapabilityString, sizeof(pPrinterPB->capability) ); break; case kDelayGetCapability: // // USS-720 USB-parallel cable: couldn't get the capability string because the printer is off // Delay a few seconds and try again. // Will succeed when the user turns the printer on. // MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kDelayGetCapability", 0); SetNullUSBParamBlock( pPrinterPB->interfaceRef, &pPrinterPB->pb ); pPrinterPB->pb.usbBuffer = 0; pPrinterPB->pb.usbActCount = 0; pPrinterPB->pb.usbReqCount = kUSS720MillisecondDelay; pPrinterPB->pb.usbFlags = kUSBTaskTimeFlag; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; pPrinterPB->delayInProgress = true; err = USBDelay(&pPrinterPB->pb); if(immediateError(err)) { USBExpertFatalError(pb->usbReference, err, kPStrPrinterDriverName"kDelayGetCapability - immediate error", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kAllocateCapabilityMem: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kAllocateCapabilityMem", 0); SetNullUSBParamBlock( pPrinterPB->interfaceRef, &pPrinterPB->pb ); length = *(UInt16*)(pPrinterPB->capability); pPrinterPB->pb.usbBuffer = 0; pPrinterPB->pb.usbActCount = 0; pPrinterPB->pb.usbReqCount = length; pPrinterPB->pb.usbFlags = 0; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; err = USBAllocMem(&pPrinterPB->pb); if(immediateError(err)) { USBExpertFatalError(pb->usbReference, err, kPStrPrinterDriverName"kAllocateCapabilityMem - immediate error", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kGetFullCapabilityString: // // the capability string was too long to fit in the statically allocated space // need to release this memory when we finalize our driver // MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kGetFullCapabilityString", 0); length = *(UInt16*)(pPrinterPB->capability); if ( pPrinterPB->pCapabilityString ) GetCapability( pPrinterPB, pPrinterPB->pCapabilityString, length ); else USBExpertFatalError(pPrinterPB->interfaceRef, kUSBInternalErr, kPStrPrinterDriverName"Can't allocate capability memory", 0); break; case kGetInterface: // failsafe check that we've got the right setup // it's possible the device didn't respond to our SetInterface GetInterface( &pPrinterPB->pb, &pPrinterPB->whichAltInterface ); break; case kFindBulkOutPipe: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kFindBulkOutPipe", 0); SetNullUSBParamBlock( pPrinterPB->interfaceRef, &pPrinterPB->pb ); pPrinterPB->pb.usbBuffer = 0; pPrinterPB->pb.usbActCount = 0; pPrinterPB->pb.usbReqCount = 0; pPrinterPB->pb.usbFlags = kUSBOut; pPrinterPB->pb.usbClassType = kUSBBulk; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; err = USBFindNextPipe( &pPrinterPB->pb ); if (immediateError(err)) { USBExpertFatalError(pPrinterPB->interfaceRef, kUSBInternalErr, kPStrPrinterDriverName"kFindBulkOutPipe - immediate error", err); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kFindBulkInPipe: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kFindBulkInPipe", 0); SetNullUSBParamBlock( pPrinterPB->interfaceRef, &pPrinterPB->pb ); pPrinterPB->pb.usbBuffer = 0; pPrinterPB->pb.usbActCount = 0; pPrinterPB->pb.usbReqCount = 0; pPrinterPB->pb.usbFlags = kUSBIn; pPrinterPB->pb.usbClassType = kUSBBulk; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; err = USBFindNextPipe( &pPrinterPB->pb ); if (immediateError(err)) { USBExpertFatalError(pPrinterPB->interfaceRef, kUSBInternalErr, kPStrPrinterDriverName"kFindBulkInPipe - immediate error", err); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kTaskTimeRequired: MyUSBExpertStatusLevel(kDebugStatusLevel, pPrinterPB->interfaceRef, kPStrPrinterDriverName"kTaskTimeRequired", 0); // to stress test usb bus, fallthrough to kGetCentronicsStatus SetNullUSBParamBlock( pPrinterPB->interfaceRef, &pPrinterPB->pb ); pPrinterPB->pb.usbBuffer = 0; pPrinterPB->pb.usbActCount = 0; pPrinterPB->pb.usbReqCount = kUSBNoDelay; pPrinterPB->pb.usbFlags = kUSBTaskTimeFlag; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; pPrinterPB->delayInProgress = true; err = USBDelay(&pPrinterPB->pb); if(immediateError(err)) { USBExpertFatalError(pb->usbReference, err, kPStrPrinterDriverName"kTaskTimeRequired - immediate error", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kGetCentronicsStatus: SetNullUSBParamBlock( pPrinterPB->interfaceRef, &pPrinterPB->pb ); CentronicsStatus( &pPrinterPB->pb, &pPrinterPB->centronics.b, pPrinterPB->interfaceNumber ); pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; err = USBDeviceRequest(&pPrinterPB->pb); if(immediateError(err)) { USBExpertFatalError(pb->usbReference, err, kPStrPrinterDriverName"kGetCentronicsStatus Immediate error", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; case kDelayGetCentronicsStatus: SetNullUSBParamBlock( pPrinterPB->interfaceRef, &pPrinterPB->pb ); pPrinterPB->pb.usbBuffer = 0; pPrinterPB->pb.usbActCount = 0; pPrinterPB->pb.usbReqCount = kUSS720StatusMSDelay; pPrinterPB->pb.usbRefcon |= kTransactionPending; pPrinterPB->pb.usbCompletion = (USBCompletion)PrinterDeviceCompletionProc; pPrinterPB->delayInProgress = true; err = USBDelay(&pPrinterPB->pb); if(immediateError(err)) { USBExpertFatalError(pb->usbReference, err, kPStrPrinterDriverName"kDelayGetCentronicsStatus - immediate error", 0); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; } break; default: USBExpertFatalError(pPrinterPB->deviceRef, kUSBInternalErr, kPStrPrinterDriverName"InitiateTransaction - unknown state", pPrinterPB->pb.usbRefcon); pPrinterPB->pb.usbRefcon |= kReturnFromDriver; break; } if (pPrinterPB->pb.usbRefcon & kReturnFromDriver) { pPrinterPB->pb.usbCompletion = (USBCompletion) NULL; pPrinterPB->pb.usbRefcon &= ~kTransactionPending; } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: PrintDriverEntry Input Parameters: Output Parameters: Description: This is where the system instantiates a USB printing device. We need to install drivers in the MacOS unitTable, and a reference in the name registry. But the information we need to do this is only available after some USB transactions have completed. So we initiate here a series of asynchronous USB operations to get that information (by calling the first ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void PrintDriverEntry( USBDeviceRef deviceRef, USBDeviceDescriptorPtr pDeviceDesc, USBInterfaceDescriptorPtr pInterfaceDesc, UInt32 interfaceNumber ) { static Boolean beenThereDoneThat = false; OSStatus err; RoutineDescriptor qw = BUILD_ROUTINE_DESCRIPTOR( uppQueueUSBWriteProcInfo, QueueWrite); RoutineDescriptor qr = BUILD_ROUTINE_DESCRIPTOR( uppQueueUSBReadProcInfo, QueueRead); RoutineDescriptor qa = BUILD_ROUTINE_DESCRIPTOR( uppAbortProcInfo, Abort); RoutineDescriptor qs = BUILD_ROUTINE_DESCRIPTOR( uppControlStatusProcInfo, ControlStatusRequests ); if( !beenThereDoneThat) { printerClassRecord.terminating = false; printerClassRecord.printerRegistered = false; beenThereDoneThat = true; //DebugStr("\pIn Printer Driver Entry"); MyUSBExpertStatusLevel(kNormalStatusLevel, deviceRef, kPStrPrinterDriverName"Starting USB Printer Driver", 0); printerClassRecord.deviceDescriptor = *pDeviceDesc; /* keep a copy of the device descriptor */ printerClassRecord.pInterfaceDescriptor = pInterfaceDesc; printerClassRecord.interfaceNumber = interfaceNumber; printerClassRecord.deviceRef = deviceRef; printerClassRecord.interfaceRef = deviceRef; printerClassRecord.transDepth = 0; /* init Delay Callback Depth */ printerClassRecord.inRefNum = -1; /* initially no DRVRs added to UnitTable */ printerClassRecord.outRefNum = -1; err = LoadResources( &printerClassRecord ); if ( err != noErr ) USBExpertFatalError( deviceRef, err, kPStrPrinterDriverName"LoadResources failed", 0); // // routines to write and read to the device must be called by 68K DRVR // so we use mixed-mode manager to dispatch between PPC and 68K // printerClassRecord.qwrite = (QueueUSBWriteUPP) &printerClassRecord.qwriteRD; printerClassRecord.qwriteRD = qw; printerClassRecord.qread = (QueueUSBReadUPP) &printerClassRecord.qreadRD; printerClassRecord.qreadRD = qr; printerClassRecord.qstatus = (ControlStatusUPP) &printerClassRecord.qstatusRD; printerClassRecord.qstatusRD = qs; printerClassRecord.qabort = (AbortUPP) &printerClassRecord.qabortRD; printerClassRecord.qabortRD = qa; printerClassRecord.r = (QueueUSBReadUPP) QueueRead; printerClassRecord.w = (QueueUSBWriteUPP) QueueWrite; printerClassRecord.s = (ControlStatusUPP) ControlStatusRequests; printerClassRecord.a = (AbortUPP) Abort; SetNullUSBParamBlock( deviceRef, &printerClassRecord.pb ); SetNullUSBParamBlock( 0, &printerClassRecord.in ); // fill in pipe ref later SetNullUSBParamBlock( 0, &printerClassRecord.out ); // fill in pipe ref later CStrCopy((char *)printerClassRecord.name, ""); CStrCopy((char *)printerClassRecord.model, ""); #if DOUBLE_BUFFER // // Assume 1. TRANSFER_SIZE is a power of 2 // Assume 2. malignedBuffer is allocated to be 3*TRANSFER_SIZE // printerClassRecord.pageWriteAlignedBufferSize = TRANSFER_SIZE; // should get this from VM printerClassRecord.pageWriteAlignedBuffer = printerClassRecord.malignedBuffer; // align it below the buffer, then bring it into the range of the buffer *((UInt32 *) &printerClassRecord.pageWriteAlignedBuffer) &= ~(printerClassRecord.pageWriteAlignedBufferSize - 1); //assumption1 *((UInt32 *) &printerClassRecord.pageWriteAlignedBuffer) += printerClassRecord.pageWriteAlignedBufferSize; //assumption2 printerClassRecord.pageReadAlignedBufferSize = TRANSFER_SIZE; printerClassRecord.pageReadAlignedBuffer = printerClassRecord.pageWriteAlignedBuffer + TRANSFER_SIZE; #endif printerClassRecord.printerConfigured = false; // // Just to be thorough, lets hold our paramter blocks so that we won't page them at // interrupt time. (We should be in the System heap and automatically held.) // HoldMemory( &printerClassRecord, sizeof(struct usbPrinterPBStruct) ); CheckUSBVersion(); // // Start out at first state // printerClassRecord.pCapabilityString = printerClassRecord.capability; printerClassRecord.pb.usbRefcon = kFindInterface_bidirectional; // don't start if we received a removal notification just as we're starting up if (printerClassRecord.terminating) { printerClassRecord.pb.usbCompletion = (USBCompletion) NULL; } else { PrinterDeviceInitiateTransaction(&printerClassRecord.pb); } } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: PrinterRemovalNotification Input Parameters: Output Parameters: Description: release any allocated storage remove DRVRs from the UnitTable One small complication happens when the USS-720 cable is used. It's possible that the user has the device plugged in, but the printer wasn't powered on. In this case, our state machine is still cycling between the states kDelayGetCapability and kGetCapabilityString. If we terminate before the delay returns we'll crash the system. We monitor terminating to handle this ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ OSStatus PrinterRemovalNotification( void ) { static Boolean logfileclosed = false; static Boolean aborted = false; static Boolean deregistered = false; static Boolean readpipeaborted = false; static Boolean writepipeaborted = false; static Boolean timedout = false; OSStatus result = noErr; static unsigned long tc = 0; // // notify state machine not to continue // printerClassRecord.terminating = true; MyUSBExpertStatusLevel(kNormalStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"Driver removal notification received", 0); if (!logfileclosed) { MyUSBExpertStatusLevel(kNormalStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"Closing printer log file", 0); LOGGING( fclose( logfile ) ); logfileclosed = true; } // if the printer was never registered, then don't try to deregister it if (!(printerClassRecord.printerRegistered)) deregistered = true; if (!deregistered) { // // remove printer from the name registry // MyUSBExpertStatusLevel(kDebugStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"Removed printer entry from nameregistry", 0); DeregisterDevice( &printerClassRecord ); deregistered = true; } // per the Mac OS USB DDK API Ref // "If the device associated with this call [USBDelay] is unplugged and its driver removed while // this function call is pending, the function will not complete." // therefore don't hang around if a delay is in progress if (( printerClassRecord.pb.usbCompletion != (USBCompletion) NULL ) && (!printerClassRecord.delayInProgress)) { MyUSBExpertStatusLevel(kDebugStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"Waiting for configuration to complete", printerClassRecord.pb.usbRefcon); return (OSStatus) kUSBDeviceBusy; } // Abort any outstanding write requests if (( printerClassRecord.out.usbCompletion != (USBCompletion) NULL ) && (!writepipeaborted)) { MyUSBExpertStatusLevel(kDebugStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"Abort write pipe", printerClassRecord.writePipeRef); USBAbortPipeByReference( printerClassRecord.writePipeRef ); writepipeaborted = true; return (OSStatus) kUSBDeviceBusy; } // Abort any outstanding read requests if (( printerClassRecord.in.usbCompletion != (USBCompletion) NULL ) && (!readpipeaborted)) { MyUSBExpertStatusLevel(kDebugStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"Abort read pipe", printerClassRecord.readPipeRef ); USBAbortPipeByReference( printerClassRecord.readPipeRef ); readpipeaborted = true; return (OSStatus) kUSBDeviceBusy; } // Abort any outstanding transactions if (!aborted) { MyUSBExpertStatusLevel(kDebugStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"Call Abort()", 0); Abort( 0, &printerClassRecord ); aborted = true; } // wait up to ten seconds for the read & write routines to complete. When they do, the completion procptrs will be NULL'd if (tc == 0) tc = TickCount() + 10*60; if ( TickCount() >= tc ) timedout = true; if (( TickCount() < tc ) && (( printerClassRecord.out.usbCompletion != (USBCompletion) NULL) || ( printerClassRecord.in.usbCompletion != (USBCompletion) NULL ))) { return (OSStatus) kUSBDeviceBusy; } // Put the appropriate timeout message in the log if ( timedout ) { MyUSBExpertStatusLevel(kNormalStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"took longer than 10 seconds for read/write pipes to complete", 0); } else { MyUSBExpertStatusLevel(kNormalStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"Pipes closed normally", 0); } if (( printerClassRecord.out.usbCompletion != (USBCompletion) NULL) || ( printerClassRecord.in.usbCompletion != (USBCompletion) NULL )) { MyUSBExpertStatusLevel(kDebugStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"Completion procs are not nil!!", 0); } // remove our unit table drivers if (printerClassRecord.outRefNum != -1 ) { TradRemoveDriver( printerClassRecord.outRefNum, false ); printerClassRecord.outRefNum = -1; } if (printerClassRecord.inRefNum != -1 ) { TradRemoveDriver( printerClassRecord.inRefNum, false ); printerClassRecord.inRefNum = -1; } // // release any allocated storage // if ( printerClassRecord.pCapabilityString != printerClassRecord.capability ) { MyUSBExpertStatusLevel(kDebugStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"Deallocated capability string memory", 0); printerClassRecord.pb.usbReference = printerClassRecord.deviceRef; printerClassRecord.pb.usbFlags = 0; printerClassRecord.pb.usbRefcon = kDeallocateCapbilityString; printerClassRecord.pb.usbBuffer = printerClassRecord.pCapabilityString; printerClassRecord.pb.usbCompletion = (USBCompletion)kUSBNoCallBack; printerClassRecord.delayInProgress = true; // this prevents the control pipe completion procptr from being checked USBDeallocMem(&printerClassRecord.pb); printerClassRecord.pCapabilityString = printerClassRecord.capability; return (OSStatus) kUSBDeviceBusy; } // // don't need to hang on to param blocks after we've gone away // UnholdMemory( &printerClassRecord, sizeof(struct usbPrinterPBStruct) ); MyUSBExpertStatusLevel(kNormalStatusLevel, printerClassRecord.deviceRef, kPStrPrinterDriverName"Ready for removal", 0); return kUSBNoErr; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: CFMInitialization Input Parameters: initBlock Output Parameters: printerClassDriverFileSpec set global Description: We use the code fragment initialization to get our filespec which LoadResources will use to open our resource fork. A peculiarity of the USB 1.0 implementation is that does not lock the driver into physical memory. As a result a driver which does not call SetDriverClosureMemory on it's fragment will likely be paged in during interrupt time and a double bus-fault will occur. The solution for this is to have the USB Expert call SetDriverClosureMemory in a future release of the USB stack. In the meantime, we call it on ourselves to lock us into physical memory. When the USB stack is revved, one call to SetDriverClosureMemory (either this one or the Expert's) will be treated as a NOP. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ OSErr CFMInitialization( CFragInitBlock *initBlock ) { // // get a reference to our file so that we can open up the resource fork later on // if ( CFragHasFileLocation( initBlock->fragLocator.where ) ) printerClassDriverFileSpec = *(initBlock->fragLocator.u.onDisk.fileSpec); return noErr; } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: MyUSBExpertStatusLevel Input Parameters: level value 1 - 5 of the status level for the message. Refer to use in USBExpertStatusLevel call ref device or interface reference associated with the message status Pascal style string status message to log value value to store along with status message. Output Parameters: returns a result of osstatus Description: MyUSBExpertStatusLevel checks the version of USB present and makes the USBExpertStatusLevel call if USB 1.2 or later is present or uses the USBExpertStatus call instead if USB 1.0 - 1.1 is present. This requires the one weak link with the USBServicesLib library file. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ OSStatus MyUSBExpertStatusLevel(UInt32 level, USBDeviceRef ref, StringPtr status, UInt32 value) { if (gUSBVersion < kUSBv12) { USBExpertStatus(ref, status, value); } else { USBExpertStatusLevel(level, ref, status, value); } } // eof